콘텐츠로 이동

Dataset Status State Machine (#464)

Mapping from Drift Classification (#382) to Dataset Status (#439).

Status names are the canonical set from ADR 0005 and kpubdata.core.status.DatasetStatus; classifications are kpubdata.core.status.DriftClassification. tests/unit/test_status_vocabulary.py fails when this page uses a name the code does not define.

State Diagram

                    ┌─────────────────────────────────┐
                    │         retired (terminal)       │
                    │   (manual transitions only)      │
                    └─────────────────────────────────┘

    ┌──────────────────────────────────────────────────────────┐
    │                     production                           │
    │              (manual promotion only)                    │
    └─────────┬────────────────┬───────────────┬──────────────┘
              │                │               │
    SCHEMA_CHANGED    RATE_LIMIT /     AUTH /
    PARAMETER_CHANGED SERVICE_DOWN /   APPLICATION_
    ENDPOINT_CHANGED  UNKNOWN (≥3)     REQUIRED (≥1)
    (≥1, immediate)   │               │
              │       │               │
              ▼       ▼               ▼
    ┌─────────────┐ ┌─────────┐ ┌──────────────────┐
    │   broken    │ │unstable │ │application_required│
    └──────┬──────┘ └────┬────┘ └──────────────────┘
           │             │
    HEALTHY (≥1)   HEALTHY (≥3)
    + fixture      │
    re-record      │ (restore previous)
           │       │
           ▼       ▼
    ┌─────────────┐ ┌─────────────────┐
    │   fixture_  │ │ previous status │
    │   verified  │ │ (or live_verif.)│
    └─────────────┘ └─────────────────┘

Transition Rules

Current Status Drift Classification Streak Next Status
production RATE_LIMIT, SERVICE_DOWN, UNKNOWN 1–2 (no change, record only)
production RATE_LIMIT, SERVICE_DOWN, UNKNOWN ≥3 unstable
production SCHEMA_CHANGED, PARAMETER_CHANGED, ENDPOINT_CHANGED ≥1 broken (immediate)
production AUTH, APPLICATION_REQUIRED ≥1 application_required
production NO_DATA ≥3 unstable
production RETIRED ≥1 (no change; drift issue filed — a person retires it)
live_verified (same as production) — (same as production)
fixture_verified any failure ≥1 fixture_verified (no auto downgrade)
unstable HEALTHY ≥3 restore previous_status
unstable SCHEMA_CHANGED, PARAMETER_CHANGED, ENDPOINT_CHANGED ≥1 broken (immediate)
unstable any failure (cumulative) ≥7 broken
broken HEALTHY ≥1 fixture_verified (requires fixture re-record)
application_required HEALTHY ≥1 restore previous_status (application approved)
retired all inputs — (no transitions; manual only)

Core Principles

  1. Schema/Parameter/Endpoint changes break immediately — user code breaks, no delay
  2. Transient failures (429, 5xx) use a 3-strike rule — prevents false positives. The number is TRANSIENT_FAILURE_STREAK; LIVE_PROBE.md files its drift issue on the same count
  3. broken never auto-restores to production — a human must re-record fixtures and pass contract tests
  4. retired is terminal — only manual transitions

Decisions

previous_status storage

Yes — stored in fixtures/<dataset>/status_history.json alongside the existing meta. Simple, co-located with the data it describes, and inspectable without GitHub API calls.

Transition history location

fixtures/<dataset>/status_history.json:

[
  {"from": "live_verified", "to": "unstable", "reason": "SERVICE_DOWN", "streak": 3, "at": "2026-09-29"},
  {"from": "unstable", "to": "live_verified", "reason": "HEALTHY", "streak": 3, "at": "2026-09-30"}
]

Drift Issue Title convention

[drift] <dataset_id>: <from_status> → <to_status> (<classification>)
Example: [drift] datago.apt_trade: live_verified → broken (SCHEMA_CHANGED)

Labels: type:bug, epic:trust, plus severity:major for broken, severity:minor for unstable.

구현 (#625)

이 절의 결정은 scripts/drift_detect.py 가 구현하고, live-probe 워크플로의 drift 잡이 매일 밤 실행한다. 드리프트 상태(스트릭·previous_status·누적 실패)는 drift-state.json 아티팩트로 밤사이 이어지고, 전이가 일어나면 위 규약대로 이슈를 연다 — 같은 데이터셋에 열려 있는 [drift] 이슈가 있으면 다시 열지 않는다. status_history.json 기록은 --write-history 에서 담당하며, nightly 에는 끈다: 게이트가 저장소에 쓸 수는 없으므로 이슈 본문에 추가할 항목을 싣고, 사람(또는 에이전트)이 적용한다. RETIRED 는 상태를 움직이지 않지만 이슈는 연다 — 폐기는 사람의 판단이다.

Pure Function

def transition(
    current: DatasetStatus | str,
    classification: DriftClassification | str,
    streak: int,
    cumulative_failures: int = 0,
    previous_status: DatasetStatus | str | None = None,
) -> DatasetStatus:
    """Compute the next dataset status from drift signal."""

Implemented as kpubdata.core.status.transition() (#625), with the vocabulary (DatasetStatus, DriftClassification, TRANSIENT_FAILURE_STREAK). restore previous_status falls back to live_verified when there is none, or when it names a fault. tests/unit/core/test_status_transition.py runs every row of the table above against the function, so the table and the code cannot drift apart.