# Team Lead — Phase 5 Handover Package (Managed Agents)

> Complete standalone handover. A new engineer can continue Phase 5 with Claude +
> Make.com **without reading any prior conversation**. Every statement is
> repository-proven and cites the actual file/class/method/migration/test/commit.
> Anything not provable from the repo is marked `NOT VERIFIED IN REPOSITORY`.
> **No code was changed to produce this document.**

**Repo:** Azure DevOps `https://dev.azure.com/Lun-Dev/numu/_git/numu` · **Branch:** `feat/agent-knowledge-cache` (not pushed, no PR).
**Stack (verified):** Laravel 12.58, PHP 8.2.12, **MariaDB 10.4.32**, queue+cache=`database`. **Tests:** 79 Agents feature tests passing.

---

# 1. Executive Summary

**What Phase 5 implemented.** A registry-driven, immutable **Knowledge Cache** + **Loader**; a **propose-only Agent Runtime** (`POST /api/v1/ai/agent-runtime/run`) that loads knowledge only from the cache, builds a PII-minimized prompt, calls Anthropic, validates structured output (fail-closed + one repair), and stamps 12 traceability fields; and **production-grade idempotency** at every hop (run-create, decision, approval, execution, handoff, policy) feeding the **existing** Numu decision/policy/approval/execution pipeline. Make is orchestration-only.

**What was verified.** 79 automated tests; a **live `claude-sonnet-5` smoke test**; and a **controlled full-chain E2E** (create run → knowledge meta → runtime live → decision → handoff), both rolled back (0 rows persisted), idempotent at every hop. Final audit re-verified all invariants (`FINAL_PHASE_5_AUDIT.md`).

**What remains.** External **Make.com scenario wiring** (no backend change) + operational/product approvals (move_to_review policy, provider no-training setting, prod duplicate pre-check, incident/rotation ownership).

**Backend-complete parts.** Knowledge Cache, Loader, Runtime (+validator+repair+traceability), Anthropic adapter, Handoff P1 + idempotency + supersede, G8 policy guard, decision/approval/execution idempotency. **0 backend-code blockers.**

**Make.com-only parts.** All scenarios (trigger → create run → runtime → decision → handoff), retry orchestration, environment mapping. Make must never call the model, assemble prompts, validate output, decide policy, or execute.

**Verdict:** `PHASE_5_COMPLETE_WITH_KNOWN_LIMITATIONS` · confidence 92/100.

---

# 2. Repository Information

| Item | Value |
|---|---|
| Branch | `feat/agent-knowledge-cache` |
| Baseline commit | `a9fa600` (registry Loader refactor + Handoff P1; on top of `4b4854f`/`33d8e49` Knowledge Cache base) |
| Current HEAD | `6d6655c` (`docs: Phase 5 handover package`) |
| Working tree | clean **except** this file (`docs/managed-agents/TEAM_LEAD_PHASE_5_HANDOFF_PACKAGE.md`), currently **uncommitted** |
| Uncommitted | only this handover doc; no uncommitted **code** changes |
| Pushed / PR | No / No |

**All Phase 5 commits (chronological, since baseline `a9fa600`):**

| # | Commit | Summary |
|---|---|---|
| 1 | `29f916a` | Agent Runtime endpoint (propose-only) — Task #3 |
| 2 | `47621d4` | docs: repository question answers + reconciliation |
| 3 | `e6fbfc7` | docs: live smoke test + env/handoff analyses + Phase 5 plan |
| 4 | `4e9c236` | docs: G8 intake_triage policy analysis |
| 5 | `dde6349` | G8 active-policy uniqueness safeguard |
| 6 | `8e892f9` | Handoff idempotency / replay protection (200) |
| 7 | `2aa01b7` | docs: Make ↔ Numu orchestration contract |
| 8 | `83ea211` | docs: decision idempotency analysis |
| 9 | `51d9bb8` | Decision idempotency (Option B + D) |
| 10 | `eaed88d` | docs: E2E validation report + env reconciliation plan |
| 11 | `be59a6c` | docs: Phase 5 completion report |
| 12 | `b45f0a1` | docs: final repository answers |
| 13 | `52fd42b` | docs: final Phase 5 consistency audit |
| 14 | `6d6655c` | docs: Phase 5 handover package (HEAD) |

---

# 3. Architecture Diagram (final, as implemented)

```
Make (orchestration ONLY)
  │ 1. create run  ─────────────► POST /api/v1/ai/agent-runs          [Numu: AgentRunService]
  │ 2. knowledge lookup ────────► GET  …/agents/{key}/knowledge/meta   [Numu: AgentKnowledgeReader]
  │ 3. runtime ─────────────────► POST /api/v1/ai/agent-runtime/run
  │        └── RuntimeRunner → AnthropicModelProvider → CLAUDE ◄── ***Claude runs HERE ONLY***
  │        └── StructuredOutputValidator (fail-closed + 1 repair)
  │        └── stamp traceability (single AgentRun write)
  │        └── return PROPOSAL (executed:false)  ◄── ***Runtime STOPS HERE***
  │ 4. decision ────────────────► POST /api/v1/ai/agent-runs/{run}/decision  ◄── ***Decision pipeline BEGINS; Make loses authority***
  │        └── AgentDecisionService::decide  (SOLE entry to business action)
  │             → ActionPolicyService::resolve
  │             → { always_allow → ExpectedStateValidator → ExecutionRequestService
  │                                → AgentActionExecutor  ◄── ***Execution happens HERE (Numu)***
  │               | needs_approval → ApprovalService (PENDING)
  │               | blocked }
  │ 5. approval (if needed) ────► POST /api/v1/ai/approvals/{id}/approve|reject  [human]
  │        └── ApprovalExecutionService → AgentActionExecutor (post-approval execute)
  │ 6. handoff (move_to_review) ► POST /api/v1/ai/handoffs (idempotent) → GET …/handoffs/latest
  │ 7. audit ───────────────────► activity_logs (every step; correlation IDs)
```

- **Claude runs:** only inside `AnthropicModelProvider::complete` (`app/Services/Agents/Runtime/Provider/AnthropicModelProvider.php`), invoked by `RuntimeRunner`. Nowhere else.
- **Runtime stops:** at the returned proposal (`RuntimeRunner::run` → `'executed' => false`); its only write is a traceability `save()` (`RuntimeRunner.php:209`).
- **Decision pipeline begins:** at `POST /agent-runs/{run}/decision` → `AgentDecisionService::decide` (`app/Services/Agents/AgentDecisionService.php`).
- **Execution happens:** `AgentActionExecutor::executeForRun` (`app/Services/Agents/AgentActionExecutor.php:48`), triggered by `AgentDecisionService::executeNow` (always_allow) or `ApprovalExecutionService` (post-approval). One engine.
- **Make stops having authority:** the moment it POSTs the decision. From there Numu owns policy/approval/execution/handoff. Make never executes, decides policy, or mutates state.

---

# 4. Backend Completion Matrix

| Feature | Implemented | Tested | Live Tested | Commit | Remaining Work |
|---|---|---|---|---|---|
| Knowledge Cache | ✅ | ✅ (27) | via E2E | `4b4854f`/`33d8e49`/`a9fa600` | none |
| Registry Loader | ✅ | ✅ | via smoke | `a9fa600` | none |
| Runtime | ✅ | ✅ (14) | ✅ (sonnet-5) | `29f916a` | none |
| Runtime Validation | ✅ | ✅ | ✅ | `29f916a` | none |
| Runtime Repair | ✅ | ✅ | not live-exercised | `29f916a` | 1 live repaired run (optional) |
| Runtime Traceability | ✅ | ✅ | ✅ (12 fields) | `29f916a`/`a9fa600` | none |
| Handoff P1 | ✅ | ✅ (17) | via E2E | `a9fa600` | none |
| Handoff Idempotency | ✅ | ✅ | ✅ | `8e892f9` | none |
| Decision Idempotency | ✅ | ✅ (6) | ✅ (E2E) | `51d9bb8` | none |
| Approval Idempotency | ✅ | ✅ | ✅ | `51d9bb8` | none |
| Execution Idempotency | ✅ | ✅ | ✅ | (baseline) | none |
| Policy Guard (G8) | ✅ | ✅ | rolled-back | `dde6349` | product sign-off on move_to_review |
| Environment Reconciliation | ➖ plan only | — | — | `eaed88d` | additive migration (deferred, post-integration) |
| Make Contract | ✅ (doc) | — | — | `2aa01b7` | Make scenarios (external) |
| E2E Validation | ✅ | ✅ | ✅ (live) | `eaed88d` | AUTO-025 eval fixture (external) |

---

# 5. API Reference for Make

Base: `https://<host>/api/v1/ai`. Envelope: success `{data, meta:{request_id,correlation_id,actor}}`; error `{error:{code,message,details}}` (`AgentApiController`). Middleware for all below: **`McpAuth:full` + `ai.cb` + `throttle:600,1`** unless noted (`routes/api.php`). Auth header: `Authorization: Bearer <token>` — OAuth `numu:write` **or** Sanctum `numu:full`. **Environment mapping:** runtime/knowledge use `production`; decision uses `prod` — map `production↔prod`, `staging↔test` (§11).

### 5.1 Health check
- **GET** `/_ping/read` (ability `numu:read`) · **GET** `/_ping/full` (ability `numu:full`) — `routes/api.php:29,40`.
- Response: `{ok:true, channel, actor:{id,email}, abilities:[]}`. Idempotent read. Retry: safe.

### 5.2 Create run
- **POST** `/agent-runs` — `AgentRunController::store`.
- Request: `{ startup_id | investor_id (exactly one), agent_key, idempotency_key, trigger_type?, correlation_id?, knowledge_version?, knowledge_checksum?, knowledge_package_id?, knowledge_registry_version?, context?, expected_group?, expected_status? }`.
- Response: `201` (new) / `200` (existing) `{data: run}`.
- **Idempotency:** same `idempotency_key` → same run (`AgentRunService::create`). **Retry:** safe (same key). **Failure:** 422 validation. **Owner:** Make.

### 5.3 Get run (state check for retries)
- **GET** `/agent-runs/{run}` — `AgentRunController::show`. Response `{data: run + approvalRequests + executionRequests}`. Read; retry safe. **Use before retrying a decision.**

### 5.4 Knowledge metadata
- **GET** `/agents/{agent_key}/knowledge/meta?environment=production` — `AgentKnowledgeController::meta`.
- Response: `{agent_key, environment, knowledge_version, package_version, registry_version, checksum, file_count, loaded_at, source_type, source_stale}`. `404` if no active package. Read; retry safe.

### 5.5 Runtime (propose)
- **POST** `/agent-runtime/run` — `AgentRuntimeRunController::run`.
- Request: `{ agent_key, startup_id, agent_run_id, environment(=production) }`.
- Response `200`: `{ agent_key, agent_code, stage, environment, startup_id, agent_run_id, proposal:{analysis, proposed_action, confidence, reasoning_summary, proposed_note, proposed_handoff}, traceability:{knowledge_package_id, knowledge_version, knowledge_checksum, knowledge_registry_version, knowledge_loaded_at, source_stale, prompt_contract_version, runtime_provider, runtime_model, runtime_latency_ms, runtime_token_input, runtime_token_output, runtime_request_id, runtime_calls}, output_repaired, executed:false }`.
- **Failures:** `404 not_found` · `422 precondition_failed` · `409 knowledge_unavailable` · `422 invalid_model_output` · `502 provider_error`.
- **Idempotency:** side-effect-free (re-stamps traceability). **Retry:** safe (new proposal each call). **Owner:** Make.

### 5.6 Decision
- **POST** `/agent-runs/{run}/decision` — `AgentRunController::decision`.
- Request: `{ action_slug, confidence?(0..1), reason?, payload?, environment(dev|test|prod), risk_level?(none|expected|high) }`.
- Response `200`: `{ policy, policy_source, decision, executed, run, approval_id?/execution_request_id?, idempotent_replay? }`.
- **Idempotency:** one decision per `agent_run_id`; replay (even different action) → same outcome (`AgentDecisionService::priorDecision`). **Retry:** **check-before-retry** — `GET /agent-runs/{run}`; if `analysis_status` terminal, do NOT re-POST. **Failure:** 422 validation. **Owner:** Make.

### 5.7 Handoff create
- **POST** `/handoffs` — `HandoffController::store`.
- Request: `{ startup_id | investor_id, source_run_id, from_agent, to_agent?, stage, schema_version, facts?, findings?, risks?, open_questions?, documents_used?, decision_summary?, retention_class? }`.
- Response: `201` (new) / `200` (existing). **Idempotency:** logical tuple `(subject, source_run_id, from_agent, to_agent, stage, schema_version)` → 200 replay (`HandoffService::createIdempotent`). **Retry:** safe. **Failures:** 422 (semantic `validateSourceRun`), 413 (payload), 409/422 (`mapQueryException`). **Owner:** Make. Only on `move_to_review` (AUTO-025).

### 5.8 Handoff latest
- **GET** `/handoffs/latest?startup_id=&from_agent=&schema_version=` — `HandoffController::latest`. Response `{data: handoff}` / `404`. Read; retry safe.

### 5.9 Handoff supersede
- **POST** `/handoffs/{id}/supersede` — `HandoffController::supersede`. Request `{to_agent?, facts?, ...}`. Response `201` (new correction) / `409` (already superseded). **Retry:** check-before-retry (`GET /handoffs/latest`). **Owner:** Make/Backend.

### 5.10 Approvals
- **GET** `/approvals`, **GET** `/approvals/{approval}` — read.
- **POST** `/approvals/{approval}/{decide|approve|reject}` — one-shot; `409` if already settled (`ApprovalService::decide`). **Retry:** no blind retry. **Owner:** Make/Human.

### 5.11 Knowledge (metadata / runtime content)
- **GET** `/agents/{agent_key}/knowledge` — metadata + file manifest (no bodies). **GET** `/agents/{agent_key}/knowledge/runtime` — full ordered content (internal). **GET** `…/knowledge/versions`, `…/knowledge/{version}`. All read; retry safe.

### 5.12 Knowledge refresh / activate (admin — NOT Make)
- **POST** `admin/agents/{agent}/refresh-knowledge` (perm `ai.knowledge.refresh`), **POST** `admin/agents/{agent}/knowledge/{environment}/{version}/activate` (perm `ai.knowledge.rollback`) — `routes/web.php`; `AgentConsoleController`. **Owner:** Backend/Operations.

---

# 6. Required Credentials

> No secrets are printed. Each row: where configured · repository-verified? · owner · rotation.

## Numu credentials
| Credential | Where configured (repo) | Repo-verified | Owner | Rotation |
|---|---|---|---|---|
| Make service token (Sanctum `numu:full`) | minted at `admin/ai/connectors` (`routes/web.php:616`, `AiConnectorController`; perm `ai.connectors.manage`) | ✅ (mint route exists) | Operations | NOT VERIFIED IN REPOSITORY |
| OAuth bearer (`naat_`, scope `numu:write`) | `McpAuth` / `oauth_access_tokens` (`app/Http/Middleware/McpAuth.php`) | ✅ | Operations | revoke/re-mint (no cadence in repo) |
| `ANTHROPIC_API_KEY` | env → `config('services.anthropic.api_key')` (`config/services.php`) | ✅ (read; key set in dev) | Backend/Operations | NOT VERIFIED IN REPOSITORY |
| `MS_GRAPH_CLIENT_ID` / `MS_GRAPH_CLIENT_SECRET` / `MS_GRAPH_TENANT_ID` | env → `config/microsoft_bookings.php` (via `GraphTokenProvider`) | ✅ (only used if `AGENT_KNOWLEDGE_SOURCE=graph`) | Operations | NOT VERIFIED IN REPOSITORY |
| Make connection secrets (Make side) | Make.com | NOT VERIFIED IN REPOSITORY (external) | Make | Make |

## Environment variables (verified via `env(...)` in config)
| Variable | Config file | Default |
|---|---|---|
| `NUMU_AI_ENABLED` | `config/ai.php:33` | `true` (gates all `/api/v1/ai/*`) |
| `ANTHROPIC_API_KEY` | `config/services.php` | — (required for live runtime) |
| `ANTHROPIC_MODEL` | `config/services.php` | `claude-opus-4-7` (env resolves to `claude-sonnet-4-6` in dev) |
| `ANTHROPIC_BASE_URL` / `ANTHROPIC_VERSION` / `ANTHROPIC_TIMEOUT` | `config/services.php` | `https://api.anthropic.com` / `2023-06-01` / `30` |
| `AGENT_RUNTIME_PROVIDER` | `config/agent_runtime.php` | `anthropic` |
| `AGENT_RUNTIME_MODEL` | `config/agent_runtime.php` | `claude-sonnet-5` |
| `AGENT_RUNTIME_PROMPT_CONTRACT` | `config/agent_runtime.php` | `v1` |
| `AGENT_RUNTIME_MAX_OUTPUT_TOKENS` / `_MAX_OUTPUT_BYTES` / `_MAX_PROMPT_BYTES` / `_RETRIES` | `config/agent_runtime.php` | `2048` / `200000` / `800000` / `1` |
| `AGENT_KNOWLEDGE_SOURCE` | `config/agent_knowledge.php` | `local` |
| `AGENT_KNOWLEDGE_REGISTRY_PATH` | `config/agent_knowledge.php` | `config/agent_knowledge_registry.json` |
| `AGENT_KNOWLEDGE_DEFAULT_ENV` | `config/agent_knowledge.php` | `production` |
| `AGENT_KNOWLEDGE_LOCAL_PATH` | `config/agent_knowledge.php` | `storage/app/agent-knowledge` |
| `AGENT_KNOWLEDGE_MAX_FILE_SIZE` | `config/agent_knowledge.php` | `1048576` |
| `AGENT_KNOWLEDGE_GRAPH_DRIVE_ID` / `_GRAPH_ROOT` | `config/agent_knowledge.php` | — / `Numu_Workspace/...` |
| `AI_AGENT_ENV` | `config/agents.php` | `prod` |
| `MS_GRAPH_*` | `config/microsoft_bookings.php` | — |

Infrastructure (logging sink, monitoring/alerts, secret rotation, DB backups, incident owner) = **NOT VERIFIED IN REPOSITORY** (operational).

---

# 7. Required Make Scenarios

| Scenario | Trigger | Actions | Retry Rules | Status |
|---|---|---|---|---|
| Run creation | Source AgentRun completed / entity ready | `POST /agent-runs` (with `idempotency_key`) | safe (same key) | Backend ready; Make TODO |
| Runtime execution | After run created | `POST /agent-runtime/run` → receive proposal | safe (new proposal) | Backend ready (live-verified); Make TODO |
| Decision submission | After proposal | `POST /agent-runs/{run}/decision` | check-before-retry (`GET /agent-runs/{run}`) | Backend ready; Make TODO |
| Approval polling | Decision = `pending_approval` | `GET /approvals/{id}` until settled; do NOT re-POST decision | poll read; no decision re-POST | Backend ready; Make TODO |
| Handoff creation | Decision = `move_to_review` | `POST /handoffs` → `GET /handoffs/latest` verify | safe (idempotent 200) | Backend ready; Make TODO |
| Handoff supersede | Correction needed | `POST /handoffs/{id}/supersede` | check-before-retry | Backend ready; Make TODO |
| Monitoring | Continuous | read `activity_logs` / run state via `GET /agent-runs` | read | EXTERNAL (Make/Ops) |
| Alerting | On failure/error | route to alert destination | n/a | NOT VERIFIED IN REPOSITORY (Ops) |

---

# 8. Product Decisions Still Required

| Decision | Severity | Owner | Recommendation | Blocks production? |
|---|---|---|---|---|
| `move_to_review = always_allow` (auto-executes in `prod`) | High | Product | Confirm auto-advance, or `setPolicy(...,needs_approval)` | **Yes** (before live `prod` decision) |
| Provider no-training policy | High | Operations | Confirm Anthropic account setting | **Yes** (compliance) |
| Production duplicate cleanup (before guard migrations) | High | Backend/Operations | Prove 0 active dups; supersede id 3 first | **Yes** |
| Handoff creation ownership (Runtime vs Make) | Medium | Product/Make | Use Make-fallback (idempotency makes it safe) until a backend trigger exists | No (manual path works) |
| Environment reconciliation timing | Low | Backend | Do post-integration (fail-safe today) | No |
| Rollout strategy (intake_triage → AUTO-027+) | Medium | Product | intake_triage first, then broaden after clean E2E | No |
| `prescreen` vs `pre_screen` canonical | Medium | Product/Backend | Decide before second-stage rollout | No (only intake_triage live) |

---

# 9. Deployment Checklist

- [ ] **Branch/build:** deploy `feat/agent-knowledge-cache` to the target env (code + config).
- [ ] **Duplicate checks:** prove 0 active duplicates in `handoffs` / `agent_action_policies` / `agent_approval_requests`; supersede the known `id 3` handoff if present.
- [ ] **Migrations:** apply in order — `2026_07_02_150000`, `2026_07_02_160000`, `2026_07_04_120000`, `2026_07_05_120000`, `2026_07_05_130000`, `2026_07_05_140000`; verify each generated column + unique index applied.
- [ ] **Policy verification:** confirm `intake_triage` policies (`move_to_review`, `reject`) via `AgentsSeeder`; obtain product sign-off on `move_to_review`.
- [ ] **API keys:** set `ANTHROPIC_API_KEY` (+ optional per-agent model); confirm Anthropic no-training setting; (if Graph) set `MS_GRAPH_*` + `AGENT_KNOWLEDGE_SOURCE=graph` + drive id.
- [ ] **Knowledge package:** load + activate the `intake_triage` seven-file package for the target environment.
- [ ] **Make setup:** mint a dedicated `numu:full` token at `admin/ai/connectors`; build scenarios (§7); configure env mapping + retry rules.
- [ ] **Monitoring:** wire `activity_logs` to a sink; dashboards for run/decision/handoff.
- [ ] **Alerting:** configure alert destination + incident owner + incident-log location.
- [ ] **Rollback testing:** dry-run `down()` for each guard migration in staging.
- [ ] **Live smoke test:** one controlled E2E (per `E2E_VALIDATION_REPORT.md`) on a marked test record; audit-verify; roll back test data.

---

# 10. Rollback Plan

**General:** Laravel rollback is batch-based (`php artisan migrate:rollback` reverts the last batch). To target one migration: `php artisan migrate:rollback --path=database/migrations/<file>.php`. Each migration below has a working `down()`.

| Migration / feature | Rollback | Impact | Risk | Recovery |
|---|---|---|---|---|
| `2026_07_05_140000_add_active_approval_guard...` | `down()` drops `approvals_active_pending_unique` + `active_approval_key` | Removes DB backstop for one-pending-approval-per-run (app-level `decide()` short-circuit + `createFromRun` firstOrCreate still dedup sequentially) | Low–Medium (concurrent double-decide could duplicate) | re-run migration |
| `2026_07_05_130000_add_handoff_logical_idempotency_guard` | `down()` drops `handoffs_active_logical_unique` + `active_logical_key` | Removes handoff dedup backstop (app-level `createIdempotent` still dedups sequentially) | Low–Medium (concurrent race) | re-run migration |
| `2026_07_05_120000_add_active_policy_guard...` | `down()` drops `policies_active_unique` + `active_policy_key` | Removes one-active-policy-per-triple backstop (`setPolicy` still maintains it) | Low | re-run migration |
| `2026_07_04_120000_add_runtime_metrics_to_agent_runs` | `down()` drops `runtime_provider/latency_ms/token_input/token_output` | Runtime stamping of those metrics fails until re-applied | Low (nullable metadata) | re-run migration |
| `2026_07_02_160000_add_investor_sequence_unique_to_handoffs` | `down()` drops `handoffs_investor_seq_unique` | Removes investor sequence guard | Low | re-run migration |
| `2026_07_02_150000_augment_knowledge_traceability` | `down()` drops knowledge/runtime traceability columns | Runtime traceability stamping fails | Medium (feature depends on it) | re-run migration |
| **Knowledge active version** | `AgentKnowledgeLoader::activateVersion($agent,$version,$env)` (admin route) | Repoints to a prior immutable version; content untouched | Low | re-activate any version |
| **Agent runtime (pause)** | `AgentRuntimeService::pause($agentKey)` / `pauseAll()` | Halts new decisions immediately (`AgentDecisionService::pauseBlock`) | Low | `enable()` to resume |
| **Policy revert** | `ActionPolicyService::setPolicy(...)` new version | Supersedes active policy; history retained | Low | set back to prior value |
| **Deploy** | revert branch / redeploy prior build | Standard | Operational | NOT VERIFIED IN REPOSITORY (deploy tooling) |

No rollback path is destructive (immutable/versioned/pointer moves).

---

# 11. Repository Truths That Override Documentation

| Topic | Documentation says | Repository (authoritative) | Evidence |
|---|---|---|---|
| Second-stage key | `pre_screen` (5.1) | **seeds `prescreen`** (no underscore); handoff from/to_agent are free strings (no enum) | `database/seeders/AgentsSeeder.php:40` |
| Environment enums | one set implied | **two**: `dev/staging/production` (knowledge/runtime) vs `dev/test/prod` (decision/policy) | `config/agent_knowledge.php`, `AgentRunController.php:30` |
| Knowledge tables | four tables (8.1) | **one** `agent_knowledge_cache` (ordered `files_json`) | `2026_07_02_130000...`; `AgentKnowledgeCache` |
| Handoff trigger ownership | Runtime-owned default (5.7) | **no backend trigger**; Make-fallback is the only wired path | `AgentRunService` (no event); grep clean |
| `schema_version` validation | reject invalid (§9) | **not rejected** (free string ≤40) | `HandoffController::store` rules |
| `retention_class` validation | reject invalid (§9) | **not rejected** (free string ≤40, metadata-only) | `HandoffController::store`; no consumer |
| Runtime model | `claude-sonnet-5` (task) | runtime `claude-sonnet-5`; service default resolves to `claude-sonnet-4-6` (env); config fallback `claude-opus-4-7` | `config/agent_runtime.php`, `config/services.php` |
| `agent_runs.stage` | referenced | **does not exist** (stage identity = `agent_key`) | `2026_06_26_110003_create_agent_runs_table.php` |
| Deployment target | `dashboard.numuangels.net` | **NOT VERIFIED IN REPOSITORY** (`.env` `APP_URL=localhost`; no deploy config) | — |
| Duplicate handoff response | `409` (§9) | **`200` idempotent** (returns existing) | `HandoffService::createIdempotent` |

---

# 12. Final Handover Instructions (step-by-step)

**Step 1 — Verify branch.** `git checkout feat/agent-knowledge-cache`; `git log --oneline` shows the 14 Phase-5 commits (§2); HEAD `6d6655c`; `git status` clean.

**Step 2 — Verify migrations.** `php artisan migrate:status`; confirm the six Phase-5 migrations (§9 order) are present/applied; confirm columns/indexes: `active_policy_key`/`policies_active_unique`, `active_logical_key`/`handoffs_active_logical_unique`, `active_approval_key`/`approvals_active_pending_unique`, `runtime_provider|latency_ms|token_input|token_output`.

**Step 3 — Verify policies.** Run `AgentsSeeder`; confirm `intake_triage` → `move_to_review: always_allow`, `reject: needs_approval` (or the product-approved variant). Confirm 0 runs with >1 active pending approval and 0 triples with >1 active policy.

**Step 4 — Verify credentials.** Set `ANTHROPIC_API_KEY`; confirm no-training setting (external); mint a `numu:full` Make token at `admin/ai/connectors`; (if Graph) set `MS_GRAPH_*` + `AGENT_KNOWLEDGE_SOURCE=graph`. Confirm `NUMU_AI_ENABLED=true`.

**Step 5 — Build Make scenarios.** Implement §7 per `MAKE_NUMU_ORCHESTRATION_CONTRACT.md`: create run → knowledge meta → runtime → decision → (handoff on move_to_review). Enforce §5 retry rules + env mapping (`production↔prod`). Make must never call the model/decide/execute.

**Step 6 — Run controlled E2E.** On a marked safe test record (dev/staging, or controlled `prod`): run the full chain; submit the decision in a non-executing env (`test`) first; verify `GET /handoffs/latest`; audit-verify `activity_logs`; roll back test data. Reference `E2E_VALIDATION_REPORT.md`.

**Step 7 — Production rollout.** Complete §9 checklist; obtain product sign-off (§8); supersede id 3 if present; apply migrations; activate the `intake_triage` package; limited activation on marked records; monitor audit + alerts; broaden only after a clean run; defer AUTO-027+ until intake_triage is proven live.

---

### Appendix — quick index
- **Tests:** `tests/Feature/Agents/{AgentKnowledgeCacheTest(27),AgentRuntimeRunTest(14),HandoffHardeningTest(17),DecisionIdempotencyTest(6),ManagedAgentsInvariantsTest(15)}` — `php artisan test tests/Feature/Agents`.
- **Reports:** `docs/managed-agents/{REPOSITORY_QUESTION_ANSWERS, LIVE_RUNTIME_SMOKE_TEST_REPORT, HANDOFF_GAP_ANALYSIS, G8_INTAKE_TRIAGE_POLICY_ANALYSIS, DECISION_IDEMPOTENCY_ANALYSIS, ENVIRONMENT_RECONCILIATION_ANALYSIS, ENVIRONMENT_RECONCILIATION_IMPLEMENTATION_PLAN, MAKE_NUMU_ORCHESTRATION_CONTRACT, E2E_VALIDATION_REPORT, PHASE_5_COMPLETION_REPORT, FINAL_REPOSITORY_ANSWERS, FINAL_PHASE_5_AUDIT, PHASE_5_HANDOVER}.md`.
- **NOT PRESENT IN REPOSITORY:** `NUMU_AGENT_RUNTIME_DEVELOPER_QUESTIONS_AND_REQUESTS.md`; `TRAINING_EVALUATION_CASES.md` / `05 Testing` fixtures.

*Repository truth only. No redesign, no placeholders. Sufficient to continue Phase 5 without prior conversation. Not committed.*
