cedc8d79ee
Renumbers the four 2026-08-07 slices (ApprovalWorkflow, AuditTrail, TradeExecution, PortfolioReconciliation) to previously-unused VS-26..29, leaving WBS_MASTER.csv's original VS-03/04/12/14 definitions (IngestMarketDataPIT, ApplyCorporateActions, RankBuyCandidates, GenerateDailyRecommendations) untouched, per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md. While investigating, found two things not yet resolved by this commit: - DEBT-017 (duplicate ApprovalWorkflow implementation): the tested backend (ApprovalWorkflow/) is [DontRegister]'d dead code; the live one (Features/ApprovalWorkflow/, wired in Program.cs) has no dedicated tests. AEG-VS-26-01 downgraded from COMPLETED to BLOCKED in the tracker pending an architect decision on which implementation is canonical. - Features/MarketData and Features/Portfolio (VS-03/04/05/08 Market Data Ingestion Dashboard, Portfolio Rebalance, Risk Metrics, Dashboard) are a third, already-implemented-and-tested body of work entirely absent from WBS_PROGRESS_TRACKER.csv. Flagged in CURRENT_ROADMAP.md as a follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
239 lines
6.6 KiB
Markdown
239 lines
6.6 KiB
Markdown
# 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
|