# Shadow Run Infrastructure Contract (AGENTS.md v16.0)

## 1. SOURCE (Requirement Grounding)

**From CLAUDE.md:**
- § "Validation Gates (Not Yet Passed)": "252+ trading days shadow, OOS testing, PBO/DSR verification"
- § "Database & Migrations": "DbUp migrations are idempotent and checksummed; failed migration rolls back"
- § "Hangfire (Background Jobs & Scheduling)": "Hangfire executes approved Application Commands"

**From README.md:**
- "최소 252거래일 Shadow, 복수 국면 OOS, PBO/DSR"

**From research/K-ArtSell_12_2_quant_review_ko.md:**
- Historical backtest: 2018–2023 rolling OOS windows
- Minimum trading sessions: 252 (≠ calendar days)

---

## 2. SLICE SPEC (Vertical Slice Design)

### 2.1 Database Schema Migration

**Goal:** Persist immutable shadow run records with JSONB metrics  
**Non-Goal:** Real-time analytics, reporting dashboards  
**Schema:** Point-in-time safe (published_at <= cutoff)

**Table:** `model_operations.shadow_run`
```
run_id (PK, UUID)
model_id (FK)
window_start (DATE)
window_end (DATE)
status (VARCHAR: Pending|DataBackfill|Replay|EvaluationComplete|Failed)
metrics_json (JSONB: {total_return, sharpe_ratio, pbo, dsr, ...})
phase_analysis_json (JSONB)
cost_analysis_json (JSONB)
false_exit_analysis_json (JSONB)
validation_gates_json (JSONB: {pbo_under_20, dsr_above_95, cost_2x_positive, all_gates_passed})
error_message (TEXT)
created_at (TIMESTAMP)
published_at (TIMESTAMP, NULL = unpublished)
```

**Idempotency:** Migration file checksummed; duplicate runs → no-op  
**Audit:** All reads include `WHERE published_at <= @cutoff`

### 2.2 KRX Data Service

**Goal:** Fetch historical OHLCV + fee schedules from Korea Exchange  
**Non-Goal:** Real-time tick data, options data  
**Data Retention:** Cache locally (prevent rate-limit hammering)

**Contract:**
```csharp
IKrxDataService.GetDailyOhlcvAsync(
    ticker: string,           // "005930" (Samsung), "000660" (LG Chem)
    startDate: DateOnly,      // 2024-01-02
    endDate: DateOnly,        // 2026-08-02
    cancellationToken: CancellationToken)
  -> Task<IReadOnlyList<OhlcvBar>>

OhlcvBar {
    Date: DateOnly,
    Ticker: string,
    Open: decimal,
    High: decimal,
    Low: decimal,
    Close: decimal,
    Volume: long,
    Dividends: decimal (optional)
}
```

**Performance:** Cache OHLCV in memory (252 trading days × 100 tickers = ~25K rows = 2–3 MB)  
**Resilience:** Retry transient HTTP 503; log permanent 400/401/403  
**PIT Safety:** No lookback beyond requested window (prevent forward bias)

### 2.3 MarketCalendar Service

**Goal:** Validate trading sessions, exclude holidays/special closures  
**Non-Goal:** Predict market open/close times  
**Source:** KRX official calendar (holidays, market closures)

**Contract:**
```csharp
IMarketCalendarService.GetTradingSessionsAsync(
    startDate: DateOnly,
    endDate: DateOnly,
    cancellationToken: CancellationToken)
  -> Task<IReadOnlyList<DateOnly>>

// Excludes:
// - Weekends (Sat/Sun)
// - Holidays (Chuseok, Lunar New Year, etc.)
// - Special closures (KRX system maintenance, emergency)
// - Returns: Ordered list of trading-session dates (ascending)
```

**Cache:** Refresh annually (holidays are stable)  
**Determinism:** Same input → same output (no stochastic edge cases)

### 2.4 Shadow Run Endpoint

**Goal:** Trigger 252+ trading-day validation runs  
**Non-Goal:** Long-running synchronous responses  
**Pattern:** FastEndpoints + Hangfire async job

**Endpoint:**
```
POST /api/shadow-runs

Request:
{
  "model_id": "uuid",
  "window_start": "2024-01-02",
  "window_end": "2026-08-02",
  "phase_filter": "All" | "BullMarket" | "BearMarket" | "Sideways" | "HighVolatility"
}

Response (202 Accepted):
{
  "run_id": "uuid",
  "status": "queued",
  "estimated_seconds": 3600
}

// Poll: GET /api/shadow-runs/{run_id}
// Returns: { status, metrics, gates, created_at, completed_at }
```

**Idempotency:** Client sends `Idempotency-Key` header (UUID); server deduplicates  
**Authorization:** RBAC (researcher role required; no public access)

---

## 3. CONTRACT (Input/Output/Status Codes)

| Component | Input | Output | Idempotent | Rollback |
|-----------|-------|--------|------------|----------|
| DbUp Migration | Migration file checksum | Table + indexes | ✅ (checksum matches) | Manual: `ALTER TABLE DROP` + restart |
| KRX Service | Ticker + date range | OHLCV bars | ✅ (same dates = same prices) | N/A (read-only) |
| MarketCalendar | Date range | Trading session list | ✅ (deterministic) | N/A (read-only) |
| Shadow Run Endpoint | Model ID + window | Job ID (202) | ✅ (Idempotency-Key) | Hangfire: `DELETE FROM job WHERE id=X` |

---

## 4. DATA (Schema + Migration + PIT)

**Migration file:** `src/KArtSell.DbMigrator/V0008_CreateShadowRunTable.sql`

**Normalization:**
- Writes: 3NF (atomic shadow_run record)
- Reads: Denormalized JSONB (metrics/gates pre-computed)
- PIT: `WHERE published_at <= @cutoff` on all reads

**Indexes:**
- `(model_id, created_at DESC)` — Latest run lookup
- `(status)` — Query pending/failed runs
- `(published_at)` — PIT compliance

---

## 5. TESTS (Verification Levels)

| Level | Scope | Scenarios |
|-------|-------|-----------|
| Unit | OHLCV parser, MarketCalendar logic | Parse CSV → decimals; exclude holidays |
| Integration | Real DB + KRX stub | Migration idempotency; schema conformance |
| E2E | Full shadow run (small window) | Request → Job → Result persisted |
| Golden | Baseline OHLCV comparison | 2018–2023 historical vs. API (< 0.1% variance) |

---

## 6. OPS (Deployment + Monitoring)

**Deployment:**
1. DbUp migration runs at app startup
2. KRX API key from Gitea Actions Secrets
3. MarketCalendar cache warmed on app init (one-time 1–2s)

**Monitoring:**
- Job queue depth (alert if `q-research` > 10 jobs pending)
- API rate-limit tracking (KRX: 100 req/min typical)
- Data gaps detected (OHLCV missing dates) → log & alert

**Rollback:**
- Shadow run failure → log error, mark status=Failed
- Migration failure → manual SQL intervention + app restart

---

## 7. OUTPUT RULE (Deliverables)

**Changed files:**
```
src/KArtSell.DbMigrator/
  V0008_CreateShadowRunTable.sql

src/KArtSell.Modules.ModelOperations/
  Services/
    KrxDataService.cs (implement IKrxDataService)
    MarketCalendarService.cs (implement IMarketCalendarService)

src/KArtSell.Host/
  Features/ShadowRun/
    Endpoint.cs
    Request.cs
    Response.cs
    Handler.cs
    Policy.cs

tests/
  KArtSell.Integration.Tests/
    KrxDataServiceTests.cs
    MarketCalendarServiceTests.cs
    ShadowRunEndpointTests.cs
```

**Verification:**
```bash
dotnet test --filter "Category=Infrastructure" -c Release
dotnet build src/KArtSell.Host -c Release
# Migration runs at startup; no errors
```

---

## 8. AGENTS.md v16.0 CHECKLIST

| Criterion | Status | Evidence |
|-----------|--------|----------|
| **SOLID** | ✅ Design | IKrxDataService, IMarketCalendarService abstractions; DI injection |
| **Complexity** | ✅ Design | Services: Cyclomatic < 10; Endpoint: CRUD pattern |
| **Audit** | ✅ Design | shadow_run.published_at PIT safety; migration checksum |
| **Necessity** | ✅ Sourced | CLAUDE.md § "Validation Gates"; requirement: 252 trading days |
| **Normalization** | ✅ Design | 3NF writes (atomic record); JSONB denormalization for reads |
| **Simplicity** | ✅ Design | No hidden state; explicit error handling (transient/permanent) |
| **Pattern** | ✅ Design | Vertical Slice (Endpoint → Handler → Policy → Services) |
| **Guardrails** | ✅ Design | Idempotency keys; retry classification; no partial success |
| **Traceability** | ✅ Planned | CorrelationId in logs; run_id immutable artifact |
| **Safety** | ✅ Design | Idempotent migrations; job deduplication; rollback procedure |
| **Maturity** | ✅ Design | Contract → Implementation → Test sequencing |
| **Right Way** | ✅ Commit | No shortcuts; code review required |
| **Debt** | ✅ Planned | Register if any tech debt surfaces during implementation |

---

## NEXT STEPS (Sequenced)

1. **Database Schema** (Phase 1) — Contract verification
2. **KRX Data Service** (Phase 2) — Stub first, then real API
3. **MarketCalendar Service** (Phase 3) — Hardcoded calendar, then external source
4. **Shadow Run Endpoint** (Phase 4) — FastEndpoints integration
5. **Hangfire Registration** (Phase 5) — Job scheduling

Each phase:
- ✅ Verify contract
- ✅ Write tests (unit + integration)
- ✅ Implement per checklist
- ✅ Verify all tests pass
- ✅ Commit & push
