# Phase 1 서비스 계약서

## 1. 목적

이 문서는 각 서비스와 adapter가 어떤 입력을 받고 어떤 출력을 내야 하는지 정의합니다.
AI 코딩 에이전트는 이 계약을 기준으로 인터페이스를 고정해야 합니다.

## 2. 공통 원칙

- 모든 서비스는 명시적 입력 객체를 받습니다.
- 모든 서비스는 typed result 또는 typed error를 반환합니다.
- 외부 API 호출 결과는 raw 저장 이후에만 정규화됩니다.
- 서비스는 가능하면 pure function 형태를 유지합니다.
- DB write는 application layer에서 수행하고, parser/adapter core는 side-effect를 최소화합니다.

## 3. 공통 타입

### 3.1 FetchRequest

```json
{
  "source": "sec",
  "entity": "issuer|symbol|series|date",
  "key": "0000789019",
  "start_date": "2026-01-01",
  "end_date": "2026-03-31",
  "run_mode": "backfill|daily|replay"
}
```

### 3.2 FetchResult

```json
{
  "status": "success|partial|failed|skipped",
  "raw_object_ids": ["RAW::..."],
  "records_seen": 3,
  "records_written": 3,
  "warnings": [],
  "errors": []
}
```

### 3.3 NormalizeResult

```json
{
  "status": "success|failed",
  "canonical_records": [{"...": "..."}],
  "warnings": [],
  "errors": []
}
```

## 4. SEC adapter 계약

### 4.1 입력
- CIK 또는 accession
- 기간 또는 개별 filing key
- run_mode

### 4.2 출력
- submissions raw object
- filing raw object
- exhibit raw object list
- canonical document metadata

### 4.3 함수 예시

```python
def fetch_submissions(cik: str) -> FetchResult: ...
def fetch_filing(accession_no: str, cik: str) -> FetchResult: ...
def normalize_filing(raw_object_id: str) -> NormalizeResult: ...
```

### 4.4 실패 규칙
- HTTP 실패 → retryable error
- 404 → non-retryable warning
- parsing 실패 → raw는 유지, normalize fail

## 5. Alpaca adapter 계약

### 5.1 입력
- symbol list
- timeframe
- start/end

### 5.2 출력
- canonical OHLCV record list

### 5.3 함수 예시

```python
def fetch_bars(symbols: list[str], timeframe: str, start: str, end: str) -> FetchResult: ...
def normalize_bars(raw_object_id: str) -> NormalizeResult: ...
```

## 6. FRED adapter 계약

```python
def fetch_series(series_id: str, start: str | None = None, end: str | None = None) -> FetchResult: ...
```

출력:
- series metadata
- observation records

## 7. FINRA adapter 계약

```python
def fetch_daily_short_volume(trade_date: str) -> FetchResult: ...
def normalize_daily_short_volume(raw_object_id: str) -> NormalizeResult: ...
```

주의:
- symbol mapping 실패 가능
- 원본 ticker_raw는 반드시 유지

## 8. Event Parser 계약

입력:

```json
{
  "document_id": "DOC::...",
  "source_name": "sec",
  "form_type": "8-K",
  "issuer_id": "ISSUER::...",
  "filing_date": "2026-01-29",
  "accepted_at_utc": "2026-01-29T21:05:00Z",
  "text": "...document text...",
  "metadata": {
    "item_numbers": ["2.02", "7.01"],
    "exhibits": ["EX-99.1"]
  }
}
```

출력:
- `parser_event.schema.json`을 만족하는 JSON

실패:
- invalid schema → `validation_status=invalid`
- parser exception → error object 반환, event 생성 금지

## 9. Feature Builder 계약

입력:
- event row
- related market data
- optional macro/short volume rows

출력 예시:

```json
{
  "event_id": "EVT::...",
  "snapshot_name": "event_v1",
  "snapshot_version": "1.0.0",
  "feature_json": {
    "guidance_direction": "raised",
    "oneoff_penalty": 0,
    "reaction_day_return": 0.042,
    "volume_ratio_20d": 2.3
  }
}
```

## 10. Job Runner 계약

입력:
- job_name
- source_name
- run_date
- mode

출력:
- `job_runs` 테이블 row 업데이트
- 로그 스트림

상태 전이:
- pending → running → succeeded
- pending → running → partial
- pending → running → failed

## 11. 에러 객체 표준

```json
{
  "error_class": "RateLimitError",
  "message": "HTTP 429 from source",
  "retryable": true,
  "source_name": "sec",
  "entity_key": "0000789019",
  "context": {"url": "..."}
}
```

## 12. 로그 포맷 표준

```json
{
  "timestamp": "2026-03-12T10:00:00Z",
  "level": "INFO",
  "service": "sec_collector",
  "job_run_id": "...",
  "message": "fetched submissions",
  "source": "sec",
  "entity_id": "0000789019",
  "records_seen": 1,
  "records_written": 1
}
```

## 13. 재시도 계약

- source adapter의 네트워크 실패는 최대 3회 지수 백오프
- validation 실패는 재시도하지 않음
- DB deadlock/connection issue는 retryable
- schema mismatch는 raw 보존 후 failed 처리

## 14. 금지 규칙

- adapter가 임의로 심볼명을 정정하지 말 것
- parser가 raw text를 수정 저장하지 말 것
- feature builder가 원문 source를 재호출하지 말 것
- job runner가 실패를 성공으로 덮어쓰지 말 것
