# VS-26: Model Approval Workflow (Maker-Checker Governance)

**Vertical Slice:** VS-26 (Model Approval & Activation Gateway)  
**Version:** 1.0 COMPLETE  
**Date:** 2026-08-07  
**Owner:** Platform Lead + Compliance  
**Status:** ✅ READY FOR IMPLEMENTATION  
**Depends On:** VS-02 (data governance) ✅ COMPLETE

---

## 📋 User Story

**As a** platform lead / compliance officer  
**I want to** enforce maker-checker approval workflow for model activation  
**So that** only reviewed, authorized models reach production (governance compliance)

**Acceptance Criteria:**
- ✅ Maker: Creates activation proposal (model_id, effective_at, justification)
- ✅ Checker: Reviews & approves (adds evidence links: PBO/DSR/OOS)
- ✅ SRE: Activates (executes activation command, logs execution)
- ✅ State machine: DRAFT → PROPOSED → APPROVED → ACTIVE
- ✅ Audit trail: All approvals recorded with timestamp, actor, decision
- ✅ Rollback: Activation reversible (deactivate, revert to prior version)

---

## 🎯 Non-Goals

- ❌ Implement model training (belongs to separate ML slice)
- ❌ Build PBO/DSR calculation (belongs to VS-10, shadow run results)
- ❌ Handle rejection workflows (deferred; assume approve or escalate)
- ❌ Multi-level approval chains (start with 2-tier: maker + checker)

---

## 🔄 State Machine

```
┌─────────┐
│  DRAFT  │  (Maker creates proposal)
└────┬────┘
     │
     ↓
┌──────────┐
│ PROPOSED │  (Awaiting checker review)
└────┬─────┘
     │
     ├─→ APPROVED  (Checker signs off) → ACTIVE (SRE activates)
     │
     └─→ REJECTED  (Checker rejects, returns to DRAFT for revision)
```

---

## 📊 Data Schema

```sql
-- Approval proposals
CREATE TABLE model_operations.approval_proposals (
    id UUID PRIMARY KEY,
    model_id UUID NOT NULL REFERENCES model_operations.models(id),
    status VARCHAR(50) NOT NULL,  -- DRAFT, PROPOSED, APPROVED, ACTIVE, REJECTED
    created_by VARCHAR(255) NOT NULL,  -- Maker email
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    justification TEXT NOT NULL,  -- Why this model should activate
    effective_at DATE NOT NULL,  -- When to activate (if approved)
    proposed_at TIMESTAMPTZ,  -- When moved to PROPOSED
    approved_by VARCHAR(255),  -- Checker email (if approved)
    approved_at TIMESTAMPTZ,  -- When approved
    approval_notes TEXT,  -- Checker's review notes
    activated_by VARCHAR(255),  -- SRE email (if activated)
    activated_at TIMESTAMPTZ,  -- When activated
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL
);

-- Approval evidence (links to PBO/DSR/OOS artifacts)
CREATE TABLE model_operations.approval_evidence (
    id UUID PRIMARY KEY,
    approval_proposal_id UUID NOT NULL REFERENCES model_operations.approval_proposals(id),
    evidence_type VARCHAR(50) NOT NULL,  -- PBO_SCORE, DSR_METRIC, OOS_RETURN, BACKTEST_REPORT
    evidence_url TEXT NOT NULL,  -- Path to artifact (logs, files, S3 link)
    reviewer_comment TEXT,  -- Checker's interpretation
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    correlation_id UUID NOT NULL
);

-- Approval events (audit trail)
CREATE TABLE model_operations.approval_events (
    id UUID PRIMARY KEY,
    approval_proposal_id UUID NOT NULL REFERENCES model_operations.approval_proposals(id),
    event_type VARCHAR(50) NOT NULL,  -- CREATED, PROPOSED, APPROVED, REJECTED, ACTIVATED, DEACTIVATED
    actor_email VARCHAR(255) NOT NULL,
    event_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    details JSONB,  -- Event-specific details (e.g., rejection reason)
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    correlation_id UUID NOT NULL
);
```

---

## 🔐 API Contract

### POST /approvals (Create Proposal)

**Request:**
```json
{
  "modelId": "uuid",
  "effectiveAt": "2026-09-15",
  "justification": "Model passed OOS testing; PBO score 0.95 (confident)"
}
```

**Response (201):**
```json
{
  "id": "approval-uuid",
  "status": "DRAFT",
  "modelId": "uuid",
  "createdBy": "maker@company.com",
  "createdAt": "2026-08-07T10:00:00Z"
}
```

### GET /approvals (List Proposals)

**Query Params:**
- `status=PROPOSED` (filter by status)
- `modelId=uuid` (filter by model)

**Response (200):**
```json
{
  "items": [
    {
      "id": "approval-uuid",
      "modelId": "uuid",
      "status": "PROPOSED",
      "createdBy": "maker@company.com",
      "createdAt": "2026-08-07T10:00:00Z",
      "justification": "..."
    }
  ]
}
```

### POST /approvals/{id}/approve (Checker Approval)

**Request:**
```json
{
  "approvalNotes": "PBO verified, OOS metrics acceptable",
  "evidence": [
    {"type": "PBO_SCORE", "url": "s3://evidence/pbo-0.95.json"},
    {"type": "OOS_RETURN", "url": "s3://evidence/oos-returns.csv"}
  ]
}
```

**Response (200):**
```json
{
  "id": "approval-uuid",
  "status": "APPROVED",
  "approvedBy": "checker@company.com",
  "approvedAt": "2026-08-07T11:00:00Z"
}
```

### POST /models/{id}/activate (SRE Activation)

**Request:**
```json
{
  "approvalProposalId": "approval-uuid"
}
```

**Response (202 Accepted):**
```json
{
  "jobId": "activation-job-uuid",
  "status": "QUEUED",
  "activatedAt": "2026-09-15T00:00:00Z"
}
```

---

## ✅ Governance Gates

### Pre-Merge Gates

- [x] **RBAC Roles Defined:** Maker, Checker, SRE roles assigned
- [x] **Approval State Machine:** DRAFT → PROPOSED → APPROVED → ACTIVE
- [x] **Evidence Schema:** PBO/DSR/OOS evidence links defined
- [x] **Audit Trail:** All events recorded with correlation_id

### Post-Merge Validation (Deferred)

- [ ] Integration tests (proposal creation, approval flow)
- [ ] RBAC enforcement tests (maker ≠ checker)
- [ ] Activation integration (call model activation endpoint)

---

## 🛡️ Security & Compliance

**RBAC Enforcement:**
- Maker: Can create/revise proposals (own proposals only)
- Checker: Can approve proposals (any proposal, must be different user)
- SRE: Can activate approved proposals
- Audit: All actions logged with actor identity

**Compliance:**
- ✅ Maker-checker separation (prevents unilateral activation)
- ✅ Evidence linkage (traceability to PBO/DSR/OOS)
- ✅ Immutable audit trail (for regulatory review)
- ✅ Reversibility (can deactivate if issues arise)

---

## 📋 Related Specifications

- **VS-00:** PIT envelope (published_at, correlation_id, revision)
- **VS-02:** Financial security master (governance foundation)
- **VS-27:** Audit trail (event logging)
- **VS-10:** Sell decision (uses approved models)

---

**Co-Authored-By:** Claude Haiku 4.5 <noreply@anthropic.com>  
**Status:** ✅ READY FOR IMPLEMENTATION  
**Next:** VS-27 (audit trail), then Phase 2 implementation
