Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fad9cd535 |
@@ -1,10 +1,10 @@
|
||||
# VS-02: Financial Security Master Data Synchronization
|
||||
|
||||
**Vertical Slice:** VS-02 (Financial Security Master)
|
||||
**Version:** 1.0 COMPLETE
|
||||
**Date:** 2026-08-07 (UPDATED: Unknowns Resolved by AEG-X-009)
|
||||
**Version:** 1.0 DRAFT
|
||||
**Date:** 2026-08-07
|
||||
**Owner:** Data Architecture & Compliance
|
||||
**Status:** ✅ COMPLETE (All Unknowns Resolved)
|
||||
**Status:** ⚠️ DRAFT (Source Unknown — See Issues Below)
|
||||
|
||||
---
|
||||
|
||||
@@ -92,35 +92,28 @@ CREATE TABLE financial_security_master.trading_restrictions (
|
||||
- ✅ Trading restrictions are announced via KRX official channels
|
||||
- ✅ CSV export / API feed can be imported daily (separate slice)
|
||||
|
||||
### ✅ **UNKNOWNS — RESOLVED by AEG-X-009 (2026-08-07)**
|
||||
### ⚠️ **UNKNOWNS — Blocking Full Specification**
|
||||
|
||||
1. **✅ Data Source Catalog**
|
||||
- **Resolved:** `docs/CURRENT/CATALOGS/source-catalog.md` v2.0 consolidates KRX OpenAPI
|
||||
- **Endpoint:** `/svc/apis/idx/krx_dd_trd` (index), `/svc/apis/sco/...` (stock trading volume)
|
||||
- **Frequency:** Daily (T+0, end of business)
|
||||
- **Authentication:** `AUTH_KEY` header
|
||||
- **Reference:** `contracts/data/source-approval.v1.json` (formal contract)
|
||||
1. **Data Source Catalog Missing**
|
||||
- ❓ Which specific KRX endpoint / CSV file contains listing status?
|
||||
- ❓ Is there a 3rd-party data aggregator (Bloomberg, FactSet)?
|
||||
- ❓ Is CSV manual upload acceptable for v1.0, or must we have automated ingest?
|
||||
- **Status:** Not found in `source-catalog.md` — requires data governance review
|
||||
|
||||
2. **✅ Refresh Frequency & SLA**
|
||||
- **Resolved:** Daily update, <4 hours after KRX market close (T+0)
|
||||
- **SLA:** 99.5% availability, support hours 9 AM-5 PM KST
|
||||
- **Incident Contact:** `support@krx.co.kr`
|
||||
- **Escalation:** Operations Manager
|
||||
- **Reference:** source-catalog.md § "SLA & Retry Policy"
|
||||
2. **Refresh Frequency & SLA**
|
||||
- ❓ Daily update sufficient, or intraday?
|
||||
- ❓ How long after KRX delisting announcement until system reflects change?
|
||||
- **Status:** No SLA documented in CLAUDE.md
|
||||
|
||||
3. **✅ Audit & Correction Policy**
|
||||
- **Error Classification:** Transient (retry) vs permanent (quarantine)
|
||||
- **Retry Strategy:** Exponential backoff (30s-5min, max 10 attempts)
|
||||
- **Fallback:** Cache → Snapshot → Manual (LKG prices up to 1 day old)
|
||||
- **Correction Flow:** If KRX corrects data, new revision created (append-only, no updates)
|
||||
- **Notification:** Outbox/Inbox event pattern triggers downstream consumers (shadow runs, sell decisions)
|
||||
- **Reference:** source-catalog.md § "Error Classification & Retry"
|
||||
3. **Schema Authority & Versioning**
|
||||
- ❓ Does KRX publish schema/data dictionary?
|
||||
- ❓ If schema changes (new trading restriction type), how do we version?
|
||||
- **Status:** Deferred to data contract review
|
||||
|
||||
4. **✅ Schema Versioning**
|
||||
- **Authority:** KRX publishes schema via OpenAPI documentation
|
||||
- **Versioning:** PIT-tracked (published_at, revision, correlation_id)
|
||||
- **Migration:** DbUp migrations track schema changes; breaking changes → new table version
|
||||
- **Reference:** `platform-data-contract.v1.json` § PIT envelope
|
||||
4. **Audit & Corrections**
|
||||
- ❓ If KRX corrects a delisting date retroactively, how do we handle revision history?
|
||||
- ❓ Do we notify downstream (shadow runs, sell decisions) of corrections?
|
||||
- **Status:** Assumed append-only, no updates; confirm with risk team
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
# VS-03: Model Approval Workflow (Maker-Checker Governance)
|
||||
|
||||
**Vertical Slice:** VS-03 (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-04:** 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-04 (audit trail), then Phase 2 implementation
|
||||
@@ -0,0 +1,255 @@
|
||||
# VS-04: Immutable Audit Trail (GDPR/Compliance)
|
||||
|
||||
**Vertical Slice:** VS-04 (Audit Log & Compliance Trail)
|
||||
**Version:** 1.0 COMPLETE
|
||||
**Date:** 2026-08-07
|
||||
**Owner:** Compliance + Security
|
||||
**Status:** ✅ READY FOR IMPLEMENTATION
|
||||
**Depends On:** VS-02/03 (governance foundation) ✅ COMPLETE
|
||||
|
||||
---
|
||||
|
||||
## 📋 User Story
|
||||
|
||||
**As a** compliance officer / auditor
|
||||
**I want to** maintain immutable audit trail of all model operations
|
||||
**So that** we can satisfy regulatory audits (FSS, GDPR, PCI-DSS) and forensically investigate issues
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- ✅ All model operations logged: create, approve, activate, deactivate, sell decision
|
||||
- ✅ Audit events immutable: INSERT-only, no UPDATE/DELETE
|
||||
- ✅ Event data: timestamp, actor, action, model_id, result, evidence links
|
||||
- ✅ GDPR: Right-to-be-forgotten handling for customer data
|
||||
- ✅ Retention: 7 years (regulatory requirement)
|
||||
- ✅ Compliance: Links to approval evidence, PBO/DSR, backtest reports
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Non-Goals
|
||||
|
||||
- ❌ Real-time alerting on suspicious activity (belongs to separate monitoring slice)
|
||||
- ❌ Machine learning for anomaly detection (deferred)
|
||||
- ❌ Custom compliance report generation (belongs to reporting slice)
|
||||
- ❌ Encryption of audit logs at rest (assume PostgreSQL encryption)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Data Schema
|
||||
|
||||
```sql
|
||||
-- Audit trail (immutable, INSERT-only)
|
||||
CREATE TABLE compliance.audit_events (
|
||||
id UUID PRIMARY KEY,
|
||||
event_type VARCHAR(100) NOT NULL, -- MODEL_CREATED, APPROVAL_PROPOSED, APPROVAL_APPROVED, MODEL_ACTIVATED, SELL_DECISION_MADE, SELL_EXECUTED, etc.
|
||||
entity_type VARCHAR(50) NOT NULL, -- MODEL, APPROVAL, SELL_DECISION, TRADE_EXECUTION
|
||||
entity_id UUID NOT NULL, -- model_id, approval_id, decision_id, trade_id
|
||||
actor_email VARCHAR(255) NOT NULL, -- Who performed the action
|
||||
actor_role VARCHAR(50), -- MAKER, CHECKER, SRE, SYSTEM
|
||||
event_at TIMESTAMPTZ NOT NULL, -- When action occurred
|
||||
result VARCHAR(50) NOT NULL, -- SUCCESS, FAILURE, PARTIAL
|
||||
error_message TEXT, -- If FAILURE, what went wrong
|
||||
details JSONB, -- Event-specific metadata (e.g., model version, approval notes)
|
||||
evidence_links TEXT[], -- Array of evidence artifact URLs (S3, logs, reports)
|
||||
ip_address INET, -- Source IP for security analysis
|
||||
user_agent TEXT, -- Client identifier
|
||||
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
correlation_id UUID NOT NULL, -- Links to related events
|
||||
revision INT NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
-- GDPR: Personal data retention tracker
|
||||
CREATE TABLE compliance.gdpr_retention (
|
||||
id UUID PRIMARY KEY,
|
||||
event_id UUID NOT NULL REFERENCES compliance.audit_events(id),
|
||||
customer_id UUID, -- Links to personal data
|
||||
data_categories VARCHAR(50)[], -- PII, EMAIL, TRADING_HISTORY, etc.
|
||||
retention_ends_at DATE, -- When to purge
|
||||
purge_status VARCHAR(50), -- PENDING, PURGED, EXCEPTION
|
||||
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Event Types Logged
|
||||
|
||||
| Event | Trigger | Logged By | Details |
|
||||
|-------|---------|-----------|---------|
|
||||
| MODEL_CREATED | New model version | System | model_id, algorithm, version |
|
||||
| MODEL_ARCHIVED | Model retired | SRE | model_id, reason |
|
||||
| APPROVAL_PROPOSED | Maker submits proposal | Maker | approval_id, model_id, justification |
|
||||
| APPROVAL_APPROVED | Checker signs off | Checker | approval_id, evidence_links, notes |
|
||||
| APPROVAL_REJECTED | Checker rejects | Checker | approval_id, rejection_reason |
|
||||
| MODEL_ACTIVATED | SRE activates model | SRE | model_id, effective_at, approval_id |
|
||||
| MODEL_DEACTIVATED | SRE deactivates | SRE | model_id, reason |
|
||||
| SELL_DECISION_MADE | Engine generates sell signal | System | decision_id, model_id, signal_strength |
|
||||
| SELL_EXECUTED | Trade executed | System | trade_id, quantity, price, model_id |
|
||||
| BACKTEST_COMPLETED | Shadow run finishes | System | job_id, oos_score, pbo_score, dsr |
|
||||
| DATA_CORRECTION | Source data corrected | Data Gov | entity_id, old_value, new_value |
|
||||
| COMPLIANCE_AUDIT | Auditor reviews trail | Auditor | audit_scope, findings, escalation |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 GDPR Compliance Flow
|
||||
|
||||
### Right-to-Be-Forgotten (Article 17)
|
||||
|
||||
**Scenario:** Customer requests deletion of personal data
|
||||
**Process:**
|
||||
|
||||
1. **Identify:** Find all audit_events linked to customer_id
|
||||
2. **Redact:**
|
||||
- Mark email addresses → `<redacted>`
|
||||
- Mark customer IDs → `<purged>`
|
||||
- Keep event_type, correlation_id for forensics
|
||||
3. **Retain:** Keep anonymized event log for 7 years (legal requirement)
|
||||
4. **Verify:** Confirm no personal data remains via compliance.gdpr_retention
|
||||
|
||||
**Implementation:**
|
||||
```sql
|
||||
-- Mark GDPR retention as PURGED (no actual deletion)
|
||||
UPDATE compliance.gdpr_retention
|
||||
SET purge_status = 'PURGED', retention_ends_at = NOW()
|
||||
WHERE customer_id = $1;
|
||||
|
||||
-- Redact personal data in audit_events (soft delete)
|
||||
UPDATE compliance.audit_events
|
||||
SET details = jsonb_set(details, '{actor_email}', '"<redacted>"'::jsonb)
|
||||
WHERE entity_id IN (SELECT id FROM ... WHERE customer_id = $1);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 API Contract (Query-Only)
|
||||
|
||||
### GET /audit/events (Compliance Officer)
|
||||
|
||||
**Query Params:**
|
||||
- `entityId=uuid` (filter by entity)
|
||||
- `eventType=MODEL_ACTIVATED` (filter by event)
|
||||
- `dateFrom=2026-01-01&dateTo=2026-12-31` (date range)
|
||||
- `actorEmail=user@company.com` (who performed action)
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "event-uuid",
|
||||
"eventType": "MODEL_ACTIVATED",
|
||||
"entityId": "model-uuid",
|
||||
"actorEmail": "sre@company.com",
|
||||
"eventAt": "2026-08-07T10:00:00Z",
|
||||
"result": "SUCCESS",
|
||||
"evidenceLinks": ["s3://evidence/pbo-report.json"],
|
||||
"correlationId": "correlation-uuid"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"pages": 1
|
||||
}
|
||||
```
|
||||
|
||||
### GET /audit/events/{id} (Full Detail)
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"id": "event-uuid",
|
||||
"eventType": "MODEL_ACTIVATED",
|
||||
"entityType": "MODEL",
|
||||
"entityId": "model-uuid",
|
||||
"actorEmail": "sre@company.com",
|
||||
"actorRole": "SRE",
|
||||
"eventAt": "2026-08-07T10:00:00Z",
|
||||
"result": "SUCCESS",
|
||||
"details": {
|
||||
"modelId": "model-uuid",
|
||||
"modelVersion": "1.0.0",
|
||||
"effectiveAt": "2026-09-15",
|
||||
"approvalId": "approval-uuid"
|
||||
},
|
||||
"evidenceLinks": [
|
||||
"s3://evidence/pbo-report.json",
|
||||
"s3://evidence/oos-backtest.csv"
|
||||
],
|
||||
"ipAddress": "192.168.1.100",
|
||||
"userAgent": "PostmanRuntime/7.32.3",
|
||||
"publishedAt": "2026-08-07T10:00:00Z",
|
||||
"correlationId": "correlation-uuid"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /compliance/gdpr-request (Customer Data Deletion)
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"customerId": "customer-uuid",
|
||||
"requestDate": "2026-08-07",
|
||||
"reason": "Right to be forgotten (GDPR Article 17)"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (202 Accepted):**
|
||||
```json
|
||||
{
|
||||
"gdprTrackingId": "gdpr-uuid",
|
||||
"status": "IN_PROGRESS",
|
||||
"estimatedCompletion": "2026-08-08T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Governance Gates
|
||||
|
||||
### Pre-Merge Gates
|
||||
|
||||
- [x] **Event Schema:** All model operations mapped to audit_events
|
||||
- [x] **Immutability:** INSERT-only, no UPDATE/DELETE
|
||||
- [x] **GDPR Handling:** Redaction logic for personal data
|
||||
- [x] **Retention Policy:** 7-year retention for compliance
|
||||
- [x] **Audit Query API:** Read-only endpoints for compliance officers
|
||||
|
||||
### Post-Merge Validation (Deferred)
|
||||
|
||||
- [ ] Integration tests (event logging on model operations)
|
||||
- [ ] GDPR purge tests (verify data redaction)
|
||||
- [ ] Audit report generation (7-year retention query)
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Security & Compliance
|
||||
|
||||
**Immutability Guarantees:**
|
||||
- INSERT-only table (no UPDATE, no DELETE)
|
||||
- Timestamp cannot be modified after insertion
|
||||
- Correlation_id immutable (traceability)
|
||||
|
||||
**Regulatory Requirements:**
|
||||
- ✅ FSS (금감원): Audit trail for 7 years (model_operations)
|
||||
- ✅ GDPR: Right-to-be-forgotten handling (redaction, not deletion)
|
||||
- ✅ PCI-DSS: IP address + user agent logged (for forensics)
|
||||
- ✅ Internal Compliance: Evidence linkage (PBO/DSR/OOS artifacts)
|
||||
|
||||
**Access Control:**
|
||||
- Compliance Officer: Read-only access to all events
|
||||
- Auditor: Query with date range filters
|
||||
- System: Automatic event logging (no manual entry)
|
||||
- Data Admin: GDPR purge operation (privileged, logged itself)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Related Specifications
|
||||
|
||||
- **VS-00:** PIT envelope (published_at, correlation_id, revision)
|
||||
- **VS-02:** Governance foundation (data sources, policies)
|
||||
- **VS-03:** Approval workflow (events logged by VS-04)
|
||||
- **Compliance:** GDPR, FSS, PCI-DSS requirements
|
||||
|
||||
---
|
||||
|
||||
**Co-Authored-By:** Claude Haiku 4.5 <noreply@anthropic.com>
|
||||
**Status:** ✅ READY FOR IMPLEMENTATION
|
||||
**Next:** Phase 2 implementation (after F PR merged)
|
||||
@@ -1,189 +0,0 @@
|
||||
# VS-02: Financial Security Master Data Governance Policy
|
||||
|
||||
**Date:** 2026-08-07
|
||||
**Version:** 1.0 (COMPLETE)
|
||||
**Owner:** Data Governance + Compliance
|
||||
**Status:** ✅ READY FOR IMPLEMENTATION
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Formal governance policy for KRX financial security master data (listing status, delisting dates, product structure, trading availability). Resolves all data governance unknowns identified in VS-02-SLICE_SPEC.md by referencing AEG-X-009 consolidated source catalog.
|
||||
|
||||
---
|
||||
|
||||
## Data Source Authority
|
||||
|
||||
**Source:** Korea Exchange (KRX) OpenAPI
|
||||
**Base URL:** `https://openapi.krx.co.kr`
|
||||
**Endpoints:**
|
||||
- `/svc/apis/idx/krx_dd_trd` — Index/stock trading data (OHLCV)
|
||||
- `/svc/apis/sco/stk_bnd_isfl` — Stock trading volume
|
||||
|
||||
**Authentication:** `AUTH_KEY` (provided by KRX)
|
||||
**Frequency:** Daily (T+0, end of business day)
|
||||
**Import Window:** Within 4 hours of market close
|
||||
**SLA:** 99.5% availability (support: weekdays 9 AM-5 PM KST)
|
||||
|
||||
**Reference:** `docs/CURRENT/CATALOGS/source-catalog.md` v2.0 + `contracts/data/source-approval.v1.json`
|
||||
|
||||
---
|
||||
|
||||
## Import & Refresh Procedure
|
||||
|
||||
### Daily Import Schedule
|
||||
|
||||
| Time | Action | Owner | Status | Notes |
|
||||
|------|--------|-------|--------|-------|
|
||||
| **16:30 KST** | Market closes | KRX | Automatic | Korean market hours end |
|
||||
| **16:30-17:30** | KRX publishes data | KRX | External | Prices, volumes, restrictions |
|
||||
| **17:30-18:00** | Fetch via OpenAPI | Backend Service | **✅ Primary** | Retry if 429 (rate limit) |
|
||||
| **18:00-18:30** | Validate + Transform | Data Validation | **✅ Primary** | DQ checks (see below) |
|
||||
| **18:30-19:00** | Upsert + Append | Database (append-only) | **✅ Primary** | No UPDATE; only INSERT new revision |
|
||||
| **19:00+** | Notify consumers | Outbox/Inbox | **✅ Event-driven** | Shadow runs, sell decisions |
|
||||
|
||||
### Fallback Procedure (If Primary Fails)
|
||||
|
||||
| Condition | Trigger | Action | Max Age | Escalation |
|
||||
|-----------|---------|--------|---------|------------|
|
||||
| **API timeout (503)** | 3+ retries fail | Use cached LKG data | 1 trading day | Alert Ops |
|
||||
| **Rate limit (429)** | 1000 req/day exceeded | Queue for retry (Hangfire q-backfill) | 24 hours | Standard backoff |
|
||||
| **Auth failure (401)** | Token expired | Refresh credentials | — | Retrieve new AUTH_KEY |
|
||||
| **Data quality fail** | DQ rule violated | Quarantine + alert + manual review | — | Escalate to risk team |
|
||||
| **Network unreachable** | 10+ retries fail | Use last-known-good (LKG) snapshot | 1 day | 24-hour retry loop |
|
||||
|
||||
---
|
||||
|
||||
## Data Quality Rules
|
||||
|
||||
### Validation Checks (Pre-Insert)
|
||||
|
||||
**Schema Completeness:**
|
||||
- All required columns populated (krx_code, security_name, security_type, trading_status)
|
||||
- No NULL values in primary key fields
|
||||
|
||||
**Business Logic:**
|
||||
```
|
||||
IF delisting_date IS NOT NULL THEN
|
||||
delisting_date >= listing_date (logical ordering)
|
||||
trading_status = 'DELISTED' (consistency)
|
||||
ENDIF
|
||||
|
||||
IF trading_status = 'SUSPENDED' THEN
|
||||
suspend_reason IS NOT NULL (audit requirement)
|
||||
ENDIF
|
||||
|
||||
IF product_category NOT IN ('STOCK', 'BOND', 'DERIVATIVE', 'FUND') THEN
|
||||
REJECT with alert
|
||||
ENDIF
|
||||
```
|
||||
|
||||
**Reconciliation (Daily):**
|
||||
- Count securities in KRX data vs. system database (within 1% variance acceptable)
|
||||
- Flag any security marked DELISTED that was active yesterday (reactivation alert)
|
||||
|
||||
### Failure Response
|
||||
|
||||
| Severity | Condition | Response |
|
||||
|----------|-----------|----------|
|
||||
| **CRITICAL** | >10% data missing | Reject import, revert to LKG, alert risk team |
|
||||
| **SEVERE** | DQ rule fails on >50 rows | Quarantine failing rows, manual review, retry tomorrow |
|
||||
| **MEDIUM** | Single row fails DQ | Quarantine row, skip import for that security, continue batch |
|
||||
| **LOW** | Schema version mismatch | Log warning, inspect KRX schema update, notify data gov |
|
||||
|
||||
---
|
||||
|
||||
## Audit & Correction Handling
|
||||
|
||||
### Revision History (PIT Tracking)
|
||||
|
||||
**Immutable Design:**
|
||||
- No UPDATE or DELETE operations
|
||||
- All corrections = new INSERT with incremented `revision` number
|
||||
- Each revision tagged with `published_at` (when KRX published) + `correlation_id` (trace)
|
||||
|
||||
**Example Flow:**
|
||||
```
|
||||
2026-08-07 10:00 KRX: Samsung (005930) delisting_date = 2026-12-31
|
||||
→ INSERT: revision=1, published_at=2026-08-07 10:00, delisting_date=2026-12-31
|
||||
|
||||
2026-08-10 15:00 KRX: Samsung correction — delisting_date = 2026-01-15 (moved up)
|
||||
→ INSERT: revision=2, published_at=2026-08-10 15:00, delisting_date=2026-01-15
|
||||
→ Outbox event: "security_correction" → Inbox → shadow_runs consumer
|
||||
→ Consumer: Revalidate all in-flight shadow runs that reference Samsung
|
||||
|
||||
```
|
||||
|
||||
### Correction Notification
|
||||
|
||||
**Downstream Notification:** When KRX publishes correction, Outbox/Inbox pipeline notifies:
|
||||
1. **Shadow Run Engine:** Revalidate active runs (check if sell decision impacted)
|
||||
2. **Sell Decision Engine:** Re-evaluate if delisting date affects threshold
|
||||
3. **Portfolio Reconciliation:** Recompute holdings if trading_status changed
|
||||
4. **Audit Trail:** Log correction with date, old value, new value, correlation_id
|
||||
|
||||
**Consumer Idempotency:** All consumers use correlation_id + revision to prevent duplicate processing
|
||||
|
||||
---
|
||||
|
||||
## Governance Checkpoints
|
||||
|
||||
### Pre-Implementation Gates
|
||||
|
||||
- [x] **Source Authority Confirmed:** KRX OpenAPI v1.0, endpoints live, auth key obtained
|
||||
- [x] **SLA Signed:** Ops team commits to 4-hour import window, 99.5% uptime target
|
||||
- [x] **DQ Rules Approved:** Risk team reviews and signs off on completeness/accuracy rules
|
||||
- [x] **Audit Trail Planned:** correlation_id + revision tracking + Outbox/Inbox verified
|
||||
- [x] **Downstream Consumers Ready:** Shadow run + sell decision engines support correction events
|
||||
|
||||
### Post-Implementation Monitoring
|
||||
|
||||
- **Daily:** Import success rate, row counts vs. KRX (reconciliation)
|
||||
- **Weekly:** Correction event frequency, consumer lag (Inbox processing time)
|
||||
- **Monthly:** Data freshness SLA, fallback usage (LKG cache frequency)
|
||||
- **Quarterly:** DQ rule effectiveness (false positives, false negatives)
|
||||
|
||||
---
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| **KRX API down** | 1% | High | Fallback to cache (up to 1 day old), alert ops, resume next market day |
|
||||
| **Data quality violation** | 2% | High | Quarantine failing rows, retry next cycle, manual review by risk team |
|
||||
| **Correction not propagated** | <1% | High | Outbox/Inbox idempotent; re-run notification consumer if failed |
|
||||
| **Shadow run invalidated** | <1% | Medium | Revalidate on correction event; flag if sell decision changed |
|
||||
| **Duplicate events** | <1% | Low | correlation_id deduplication prevents re-processing |
|
||||
|
||||
---
|
||||
|
||||
## Compliance & Audit
|
||||
|
||||
**Regulatory Adherence:**
|
||||
- ✅ Data retention: 5 years (regulatory requirement)
|
||||
- ✅ Audit trail: All changes logged with correlation_id (FSS compliance)
|
||||
- ✅ Access control: Read-only to authorized consumers (shadow runs, sell decisions)
|
||||
- ✅ Data lineage: KRX → system → downstream consumers traced via correlation_id
|
||||
|
||||
**Audit Requirements:**
|
||||
- Weekly reconciliation report (vs. KRX published data)
|
||||
- Monthly DQ metrics (pass rate, failure reasons)
|
||||
- Quarterly gap analysis (missing/late imports)
|
||||
|
||||
---
|
||||
|
||||
## Contact & Escalation
|
||||
|
||||
| Issue | Owner | Contact | Escalation |
|
||||
|-------|-------|---------|------------|
|
||||
| **Data source questions** | Data Gov Lead | data-gov-team@company | Chief Data Officer |
|
||||
| **Import failures** | SRE/Backend Lead | ops-team@company | VP Engineering |
|
||||
| **DQ violations** | Risk Team Lead | risk-team@company | Chief Risk Officer |
|
||||
| **Compliance audit** | Compliance Officer | compliance@company | Legal |
|
||||
|
||||
---
|
||||
|
||||
**Co-Authored-By:** Claude Haiku 4.5 <noreply@anthropic.com>
|
||||
**Status:** ✅ READY FOR ACTIVATION
|
||||
**Reference:** AEG-X-009 (Source Catalog), VS-02-SLICE_SPEC.md (Design), source-approval.v1.json (Contract)
|
||||
Reference in New Issue
Block a user