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>
6.6 KiB
6.6 KiB
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
-- 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:
{
"modelId": "uuid",
"effectiveAt": "2026-09-15",
"justification": "Model passed OOS testing; PBO score 0.95 (confident)"
}
Response (201):
{
"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):
{
"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:
{
"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):
{
"id": "approval-uuid",
"status": "APPROVED",
"approvedBy": "checker@company.com",
"approvedAt": "2026-08-07T11:00:00Z"
}
POST /models/{id}/activate (SRE Activation)
Request:
{
"approvalProposalId": "approval-uuid"
}
Response (202 Accepted):
{
"jobId": "activation-job-uuid",
"status": "QUEUED",
"activatedAt": "2026-09-15T00:00:00Z"
}
✅ Governance Gates
Pre-Merge Gates
- RBAC Roles Defined: Maker, Checker, SRE roles assigned
- Approval State Machine: DRAFT → PROPOSED → APPROVED → ACTIVE
- Evidence Schema: PBO/DSR/OOS evidence links defined
- 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