{
  "schema_version": "aipp-snapshot-update-v1",
  "generated_at": "2026-05-15T07:25:49.533450+00:00",
  "latest_forecast_generated_at": "2026-04-29T12:10:12.830205+00:00",
  "latest_data_cutoff": "2026-05-11T01:54:54Z",
  "latest_news_cutoff": "Wed, 13 May 2026 22:11:12 GMT",
  "simulation_result_status": "unchanged",
  "simulation_result_note": "검수 통과 입력이 아직 새 예측 패키지를 바꾸지 않아 기존 최신 예측 결과를 유지했습니다.",
  "changed_inputs": [
    "evidence_manifest",
    "engine_run",
    "candidate_acceptance_readiness",
    "poll_promotion_readiness",
    "goal_completion_readiness"
  ],
  "counts": {
    "candidate_manifest_rows": 1521,
    "candidate_evidence_rows": 85,
    "accepted_poll_rows": 12,
    "news_metadata_rows": 74,
    "pledge_digest_rows": 176,
    "nec_fetch_candidate_rows": 0,
    "nec_live_candidate_rows": 0,
    "canonical_candidate_rows": 643,
    "active_canonical_candidate_rows": 626,
    "canonical_candidate_regions": 256,
    "nec_not_yet_available_requests": 0,
    "manual_review_required_events": 0,
    "persona_rows": 1000000,
    "weighted_population_total": 44519085,
    "canonical_candidate_rows_byelection": 39,
    "active_canonical_candidate_rows_byelection": 39,
    "canonical_candidate_regions_byelection": 14,
    "canonical_candidate_rows_metropolitan": 85,
    "active_canonical_candidate_rows_metropolitan": 68,
    "canonical_candidate_regions_metropolitan": 15,
    "canonical_candidate_rows_municipal": 519,
    "active_canonical_candidate_rows_municipal": 519,
    "canonical_candidate_regions_municipal": 227
  },
  "source_hashes": {
    "forecast_package": "802e5c602a3b4bf687027d8c800eebe4c0a01b46d50f1d34cd55bbe8d05af70e",
    "candidate_manifest": "3b7c979ce60e5d3e3eab80d642dd03c72843a421df16b8634df8b2d18f5c8ee3",
    "evidence_manifest": "bfebf260b385e989a688cf3fcecb942ee6e6b2cad7f724ec7046f9b9a199273f",
    "news_metadata": "dd02b074a9cebd248c14d9cd3f8e032d4692bb8e48cff7035bd53ccab2983d12",
    "pledge_digest": "e91bba51426fd81f6c5fe8b529a02a1e14b835e8e96071fcc6abee40548234c8",
    "engine_run": "629a0b9ec35d5b9afbae589048fc4348fde026ee5f0cee0e60b1f14cd390660f",
    "persona_coverage": "f9a508c97683da29eeeea940e050c11f4dfb62e384f4238565123d3c3b24aa0f",
    "population_weighting": "d901e9da7c6a726796494c68e43de4079285a98a4dfa04e61e3436e0f166c1aa",
    "population_target_readiness": "b301ddb2fa9f578d9d77f25ed27abdb25427dd095940ba5cca23114428add609",
    "candidate_acceptance_readiness": "03ff27b48584fb59536b6317ec5b6128d6b5b7f5877669aff16a9de09d96f4a6",
    "poll_promotion_readiness": "681e03d0cf4d35821a6c5706fe3a343792e383e39e90ef47d615304884269830",
    "goal_completion_readiness": "dec0933d5c41ecc521d51ba4b80ff3cd7667a82a56b9d99e76fbf304c6c0cc6b",
    "nesdc_major_poll_draft": "80b161d0c28f2dfc16b16d081f61e8afd9bc7821a5531ca11af34d512cf40c5f",
    "nesdc_major_poll_review_checklist": "58b3410a396989ef4d46e911a445fb7530099a0ac908c6282ae48e1c160fd0cd"
  },
  "candidate_coverage": {
    "schema_version": "aipp-canonical-candidate-coverage-v1",
    "election_year": 2026,
    "accepted_candidate_rows": 643,
    "provisional_candidate_rows": 0,
    "canonical_candidate_rows": 643,
    "active_canonical_candidate_rows": 626,
    "canonical_candidate_regions": 256,
    "by_office": {
      "byelection": 39,
      "metropolitan": 85,
      "municipal": 519
    },
    "by_office_detail": {
      "byelection": {
        "accepted_candidate_rows": 39,
        "provisional_candidate_rows": 0,
        "canonical_candidate_rows": 39,
        "active_canonical_candidate_rows": 39,
        "historical_or_withdrawn_candidate_rows": 0,
        "canonical_candidate_regions": 14
      },
      "metropolitan": {
        "accepted_candidate_rows": 85,
        "provisional_candidate_rows": 0,
        "canonical_candidate_rows": 85,
        "active_canonical_candidate_rows": 68,
        "historical_or_withdrawn_candidate_rows": 17,
        "canonical_candidate_regions": 15
      },
      "municipal": {
        "accepted_candidate_rows": 519,
        "provisional_candidate_rows": 0,
        "canonical_candidate_rows": 519,
        "active_canonical_candidate_rows": 519,
        "historical_or_withdrawn_candidate_rows": 0,
        "canonical_candidate_regions": 227
      }
    },
    "office_coverage_readiness": {
      "metropolitan": {
        "label": "광역단체장",
        "canonical_candidate_rows": 85,
        "active_canonical_candidate_rows": 68,
        "historical_or_withdrawn_candidate_rows": 17,
        "canonical_candidate_regions": 15,
        "minimum_candidate_regions": 1,
        "missing_candidate_regions": 0,
        "status": "ready",
        "missing_requirements": []
      },
      "municipal": {
        "label": "기초단체장",
        "canonical_candidate_rows": 519,
        "active_canonical_candidate_rows": 519,
        "historical_or_withdrawn_candidate_rows": 0,
        "canonical_candidate_regions": 227,
        "minimum_candidate_regions": 220,
        "missing_candidate_regions": 0,
        "status": "ready",
        "missing_requirements": []
      },
      "byelection": {
        "label": "재보궐",
        "canonical_candidate_rows": 39,
        "active_canonical_candidate_rows": 39,
        "historical_or_withdrawn_candidate_rows": 0,
        "canonical_candidate_regions": 14,
        "minimum_candidate_regions": 14,
        "missing_candidate_regions": 0,
        "status": "ready",
        "missing_requirements": []
      }
    },
    "acceptance_worksheet": {
      "schema_version": "aipp-candidate-acceptance-worksheet-v1",
      "path": "data/raw/manual/current_candidate_roster.csv",
      "template_command": "npm run candidate:roster:template",
      "readiness_command": "npm run candidate:roster:readiness",
      "public_output_path": "public/data/candidate-manifest.json",
      "required_columns": [
        "review_status",
        "election_year",
        "office",
        "province",
        "district",
        "candidate_name",
        "party",
        "roster_status",
        "source_kind",
        "source_url",
        "source_period",
        "review_note"
      ],
      "required_field_groups": [
        "review_status",
        "election_year",
        "office",
        "province",
        "candidate_name",
        "party",
        "roster_status",
        "source_kind",
        "source_url",
        "source_period"
      ],
      "office_required_field_groups": {
        "municipal": [
          "district"
        ],
        "byelection": [
          "district"
        ]
      },
      "accepted_office_options": [
        "municipal",
        "byelection"
      ],
      "review_status_options": [
        "accepted",
        "provisional"
      ],
      "source_kind_options": [
        "official",
        "candidate_channel",
        "party_channel",
        "major_media"
      ],
      "roster_status_options": [
        "confirmed",
        "registered",
        "nominated",
        "active",
        "withdrawn",
        "retired",
        "사퇴",
        "등록무효"
      ],
      "active_roster_status_options": [
        "confirmed",
        "registered",
        "nominated",
        "active"
      ],
      "historical_roster_status_options": [
        "withdrawn",
        "retired",
        "사퇴",
        "등록무효"
      ],
      "pre_acceptance_checks": [
        "Confirm election_year=2026 and office is municipal or byelection.",
        "Prefer NEC official registration rows for candidate identity, district, and roster status.",
        "Use fallback public sources only as provisional rows unless an official source confirms the roster fact.",
        "Record a real public source_url and source_period before changing review_status to accepted or provisional.",
        "Keep raw exports, screenshots, attachments, and reviewer notes under ignored data/raw/** paths."
      ],
      "reject_if": [
        "office is metropolitan or outside the accepted_office_options list.",
        "district is missing for a municipal or byelection row.",
        "source_url is blank, non-public, or a placeholder such as https://...",
        "source_period is missing.",
        "roster_status is not one of the documented roster_status_options.",
        "source_kind is not official but review_status is accepted.",
        "the candidate key is a duplicate that has not been manually resolved.",
        "a withdrawn or historical roster_status is being used to satisfy active coverage."
      ],
      "coverage_targets_by_office": {
        "municipal": 220,
        "byelection": 14
      },
      "active_coverage_rule": "Only complete accepted/provisional rows with active roster_status values count toward coverage targets.",
      "source_discovery": {
        "schema_version": "aipp-candidate-source-discovery-v1",
        "candidate_sources": [
          {
            "source_kind": "NEC official candidate registration API",
            "source_url": "http://apis.data.go.kr/9760000/PofelcddInfoInqireService/getPofelcddRegistSttusInfoInqire",
            "source_table": "PofelcddInfoInqireService.getPofelcddRegistSttusInfoInqire",
            "required_query_params": [
              "sgId",
              "sgTypecode",
              "sdName",
              "pageNo",
              "numOfRows",
              "_type"
            ],
            "known_query_values": {
              "sgId": "20260603",
              "sgTypecode_by_office": {
                "metropolitan": "3",
                "municipal": "4",
                "byelection": "2"
              },
              "byelection_constituency_count": 14
            },
            "operator_requirement": "Use the current 2026 election sgId and the correct NEC office/election code for municipal or byelection rows. Record the exact public source URL, source period, roster status, candidate identity, party, province, and district in the ignored worksheet.",
            "current_gap": "The keyed data.go.kr API still requires an operator key in local environments; use the official info.nec.go.kr list helper when that key is unavailable."
          },
          {
            "source_kind": "NEC official candidate list page",
            "source_url": "https://info.nec.go.kr/main/showDocument.xhtml?electionId=0020260603&topMenuId=CP&secondMenuId=CPRI03",
            "source_table": "info.nec.go.kr CPRI03 후보자 명부",
            "required_query_params": [
              "electionId",
              "electionCode",
              "cityCode",
              "sggCityCode",
              "dateCode"
            ],
            "known_query_values": {
              "electionId": "0020260603",
              "electionCode_by_office": {
                "municipal": "4",
                "byelection": "2"
              },
              "byelection_constituency_count": 14
            },
            "operator_requirement": "Use the official candidate list page when the keyed data.go.kr API is unavailable. The helper command npm run candidate:roster:nec writes only the ignored raw/manual worksheet.",
            "current_gap": "Use npm run candidate:roster:nec to query each official NEC district code; row-level candidates stay in the ignored worksheet until imported by the public-safe pipeline."
          },
          {
            "source_kind": "Reviewed public fallback",
            "source_url": "",
            "source_table": "manual review worksheet",
            "operator_requirement": "Use only when NEC official rows are unavailable or incomplete, set source_kind to candidate_channel, party_channel, or major_media, and mark rows provisional unless an official record confirms roster status.",
            "current_gap": "Fallback discovery does not create public candidate facts without complete accepted/provisional worksheet rows."
          }
        ],
        "activation_rule": "Source discovery does not import candidates. Only complete accepted/provisional rows in data/raw/manual/current_candidate_roster.csv become public candidate facts."
      },
      "missing_offices": [],
      "after_acceptance_commands": [
        "npm run data:sync-jump",
        "npm run validate:data",
        "npm run test:public-manifests",
        "npm run test:public-contracts",
        "npm run build"
      ],
      "release_verification_commands": [
        "npm run data:sync-jump",
        "npm run goal:verify",
        "git diff --check",
        "npm run scan:public-safe"
      ],
      "public_safety": "Candidate acceptance worksheets must stay in ignored raw/manual paths; public manifest exposes only accepted/provisional candidate facts and aggregate coverage."
    },
    "next_actions": [],
    "freshness_cutoff": "2026-05-15",
    "review_rule": "review_status=accepted/provisional in rosterManualNote; withdrawn rows count as canonical history but not active coverage."
  },
  "quality_flags": [
    "engine:v1.0.5-local-batch",
    "turnout:age-band-reweighted",
    "persona:full-feature-store",
    "population:weighted",
    "poll:accepted",
    "candidate:canonical-coverage"
  ],
  "engine_run": {
    "version": "kangaroo-jump-engine-v1.0.5",
    "source_manifest_hash": "46ac4e44e09e896a0912dc46c48d9557f7f198bb899e5d52e5ffebf727640a66",
    "publish_recommendation": "publish_poll_adjusted_snapshot",
    "publish_gate": {
      "status": "publish_poll_adjusted_snapshot",
      "publish_mode": "poll_adjusted",
      "forced": false,
      "public_safety_validated": true,
      "legal_poll_gate_ok": true,
      "reasons": [
        "Accepted poll rows are present and every public poll row satisfies legal/editorial display fields."
      ],
      "requirements": [
        "Run npm run validate:data before publishing.",
        "Only accepted/provisional canonical rows may affect public snapshots.",
        "Poll rows require legal/editorial display approval before poll-adjusted publish."
      ],
      "missing_requirements": [],
      "decision_factors": {
        "accepted_poll_rows": 12,
        "poll_review_queue_rows": 11,
        "poll_adjusted_gate_ready": true,
        "active_canonical_candidate_rows": 626,
        "canonical_candidate_regions": 256,
        "ready_candidate_office_count": 3,
        "missing_candidate_office_count": 0,
        "manual_review_required_events": 0
      }
    },
    "backtest_error_reference": 0.140396
  },
  "notes": [
    "검수 통과 입력과 readiness report는 갱신됐고, 공개 예측 결과 패키지는 직전 스냅샷을 유지했습니다.",
    "뉴스 메타데이터 74건과 공약 digest 176건을 최신 상태로 정리했습니다.",
    "선관위 2026 후보 API는 현재 0개 요청에서 공식 후보 데이터를 아직 반환하지 않았습니다.",
    "공개 후보 manifest에는 검수 통과/임시 승인 canonical 후보 643건, active coverage 626건, 지역 coverage 256개가 있습니다.",
    "검수 통과 여론조사 12건이 공개 evidence manifest에 반영되어 있습니다.",
    "공식 인구 기준은 official_target_ready 상태이며, 2026-04 official_csv 가중 인구 44,519,085명을 사용합니다.",
    "변경된 입력 묶음: evidence_manifest, engine_run, candidate_acceptance_readiness, poll_promotion_readiness, goal_completion_readiness"
  ],
  "release": {
    "id": "20260429T121012_830205Z-802e5c60",
    "forecast_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/forecast-package.json",
    "update_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/snapshot-update.json",
    "candidate_manifest_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/candidate-manifest.json",
    "evidence_manifest_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/evidence-manifest.json",
    "engine_run_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/kangaroo-engine-run.json",
    "population_weighting_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/population-weighting-report.json",
    "population_target_readiness_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/population-target-readiness.json",
    "candidate_acceptance_readiness_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/candidate-acceptance-readiness.json",
    "poll_promotion_readiness_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/poll-promotion-readiness.json",
    "goal_completion_readiness_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/goal-completion-readiness.json",
    "nesdc_major_poll_draft_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/nesdc-major-poll-draft.json",
    "nesdc_major_poll_review_checklist_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/nesdc-major-poll-review-checklist.md",
    "persona_coverage_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/reports/persona-coverage-report.json",
    "source_manifest_url": "/data/snapshots/20260429T121012_830205Z-802e5c60/source-manifest.json"
  }
}