# 公開 artifact 契約 v1

文言の版: 第 1.0 版（2026-10-23）。改版履歴は第 9 節の末尾にある。文言の版は `schema_version`（形の版）とは別で、文言の改版だけでは `schema_version` は変わらない。

このファイルは、公開サイトの `v1/` 以下に置かれるファイル（catalog・meta・latest・データ）の形と、利用側（consumer）が守ることを定める。
機械が読む形は同じディレクトリの JSON Schema（`catalog.schema.json`・`meta.schema.json`・`latest.schema.json`）が定める。
文章と Schema が食い違うときは、サイトを運営する側が直す。利用側は Schema の「必須・型」を信じてよい。

## 1. 範囲と版

公開するもの（サイトのルートからの相対パス）:

| パス | 中身 |
|---|---|
| `v1/catalog.json` | 公開中の dataset の一覧（入口） |
| `v1/{dataset_id}/latest/meta.json` | dataset の説明（列・型・出典・来歴）|
| `v1/{dataset_id}/latest/data.parquet`・`data.csv.gz` | データ（format は dataset ごとに宣言される。`catalog.json` の `artifacts` に載るものだけが存在する）|
| `v1/{dataset_id}/latest/latest.json` | 「最新の値」だけの小さな JSON（宣言した dataset だけ）|
| `v1/schema/` | この契約文言と JSON Schema |
| `licenses/`・`llms.txt`・`robots.txt` | 利用条件・出典の一覧と、機械向けの案内 |

`artifacts` のキー（format 名）は `parquet`・`csv.gz`・`meta`・`latest`（format の追加は additive）。

過去の版を取得する URL は無い（`latest/` だけが公開される）。再現性が必要なら、取得したファイルを利用側で保存する。

版は 2 層ある。

- **外側の形の版**: `schema_version`（整数、現在 `1`）と URL の `/v1/`。catalog・meta・latest.json のトップの形、データの表現（第 5 節）、列の型の語彙が変わる破壊的変更でだけ増え、`/v2/` が別に公開される。
- **dataset の中身の変更**: 1 つの dataset の列の削除・改名・型・nullable の変更、単位・列の意味・`freq`・`region_level`・行の範囲の変更（収録期間の短縮、地域の集合の縮小、集計単位の変更など、既にある行が別の意味になる・無くなる変更）は、外側の版を上げずに、**新しい dataset id** を作って行う（第 3 節）。

## 2. 互換規律

次は **additive**（互換）で、`schema_version` は上がらない。`catalog.json` の `schema_changelog[]` に記録される。

- 既存の object への新しいフィールドの追加
- 新しい format（`artifacts` の新しいキー）
- 語彙の追加（`region_level` の新しい値など）
- dataset の追加

`schema_changelog[]` の各要素は `{schema_version, date, dataset_id, note}`。`dataset_id` が null のものは外側の形の変更。

**公開版の Schema は、未知のフィールドと未知の列挙値を許す**。利用側は未知のものを無視すること（第 4 節）。

## 3. dataset の消え方

dataset は次の 2 通りで catalog から外れる。

1. **規約・政策による掲載停止**（提供元の規約、権利者の求め、凍結・中止など）: **予告なく即時**に外れる。
2. **それ以外の廃止**: `deprecated` が付き、**30 日以上**並行して公開された後に外れる（30 日は現在の運用値で、将来変わりうる）。

`deprecated` は `{since, message, successor_id}`（通常は null）。

- `since`: 予告を始めた日（`YYYY-MM-DD`）
- `message`: 廃止の説明
- `successor_id`: 同じ catalog にある後継の dataset id。無ければ null

dataset の中身を破壊的に変えるときは、新しい dataset id（例: `{旧 id}_v2`）を公開し、旧 id に `deprecated`（`successor_id` = 新 id）を付ける。

更新を止めた dataset（paused）は、catalog に載ったまま、前回公開したデータをそのまま配信する。このとき `carried_from_build_id` が null でない値（そのデータを作った公開の識別子）になる。更新している dataset では null。

## 4. consumer の義務

1. **未知のフィールドを無視する**。未知の format、未知の列挙値（`freq`・`region_level`・列の型など）も、壊れずに扱う。
2. 列は **header 名・列名で読む**（列の位置に依存しない）。
3. **CSV の列の型は `meta.output_schema` で与える**。型推論に頼らない。コード列（`region_code` など）は VARCHAR で、**先頭のゼロに意味がある**（`01100` を整数にしない）。
4. JSON は値で読む。**キー順・空白・改行に依存しない**。オブジェクトのキーに順序は無い。
5. **行の並びに依存しない**（並びは出力を決定的にするためのもの）。
6. artifact の `url` は **catalog の URL を基準に解決する**（`urljoin`、`new URL()` でそのまま正しい相対パスになっている）。`sha256` は **ファイルのバイト列**の sha256 で、`.csv.gz` は圧縮されたまま計算する。
7. dataset は規約・政策により**予告なく** catalog から外れることがある（第 3 節）。
8. `freq` は**時間軸の粒度**で、更新の頻度ではない。時間軸を持たない dataset は null。`region_level` は行の**最も細かい**地域区分で、より粗い集計行（全国計など）を含むことがある。地域の軸を持たない dataset は null。
9. `last_fetched_at` は**データの取得時刻**（UTC）で、build や公開の時刻ではない。

## 5. データの表現

### 5.1 時間軸と地域

- `freq`: `A`（年）・`Q`（四半期）・`M`（月）・`W`（ISO 週、月曜開始）・`D`（日）。`period{from, to}` は `freq` のコード（`YYYY`・`YYYY-Qn`・`YYYY-MM`・`YYYY-Wnn`・`YYYY-MM-DD`）で書かれ、時間軸が無ければ null。
- `region_level`: `national`・`prefecture`・`municipality`（政令市の区を含む）・`mesh`・`country`、または null。語彙は増えうる（additive）。
- `lane`: `commercial` または `noncommercial`。`commercial` は、依存するすべての出典が商用利用を許している dataset。`noncommercial` は、商用利用が許されない出典を含む dataset で、商用の利用側は取り込まない。

### 5.2 型

| 型 | CSV | JSON（latest.json） |
|---|---|---|
| VARCHAR | RFC 4180（必要なときだけ quote） | string |
| BIGINT | 10 進整数 | number（整数。±(2^53−1) の範囲）|
| DOUBLE | 最短で往復できる 10 進（`1e-05` のような指数表記を含む） | number |
| DECIMAL(p,s) | 固定小数点の 10 進文字列 | string（精度保持）|
| DATE | `YYYY-MM-DD` | string |
| TIMESTAMP | UTC の `YYYY-MM-DDTHH:MM:SSZ` | string（同左）|
| BOOLEAN | `true` / `false` | `true` / `false` |
| NULL | 空のフィールド | `null` |

- CSV は UTF-8・BOM なし・LF・gzip（mtime=0）。header 行は `output_schema` の列名で、宣言の順。
- **VARCHAR の空文字は出力に現れない**。CSV の空のフィールドは NULL だけを表す。
- BIGINT は ±(2^53−1) を超えない（JavaScript で読んでも安全）。
- データ由来の時刻は UTC の `Z`（`YYYY-MM-DDTHH:MM:SSZ`）。
- Parquet の列はすべて OPTIONAL で、nullable の宣言は `meta.output_schema` にだけある。

### 5.3 JSON の規則

- map（`artifacts` のように format 名をキーにするもの）は、ある項目だけキーを持つ。
- レコードのフィールドは常に出し、該当しなければ `null`（キーが無いことはない）。
- `schema_hash` と `latest_schema_hash` は、列の `name`・`type`・`nullable` の 3 つだけから計算される（`description`・`unit`・`indicator_id` は表示用で、hash に入らない）。

### 5.4 時間軸の列

時間軸を持つ dataset（`freq` が null でない）は、`output_schema` に時間軸の列を 1 つ持つ。

- `freq` が `A` のとき、列は BIGINT の年（列名は `year`。年度のときは `fiscal_year` で、開始年の暦年）。
- `freq` が `Q`・`M`・`W`・`D` のとき、列は DATE で、値は**期間の初日**（四半期の初日、月初、ISO 週の月曜、その日）。列名は `period`。

時間軸の列の名前は `meta.json` の `dataset.period_column` にある（時間軸を持たない dataset は null）。上の列名は慣習で、機械で引くときは `period_column` を使い、型と説明は `output_schema` の同じ名前の列を見る。時間軸の列は nullable=false。catalog の `period{from, to}` はこの列の最小・最大を `freq` のコードで書いたもの。

### 5.5 地域コード

`region_code` 列は VARCHAR で、次の体系を使う。

- 全国 = `00000`
- 都道府県・市区町村 = 全国地方公共団体コードの 5 桁（都道府県は下 3 桁が `000`）。市区町村は、データの取得時点（`data_version`）で有効なコードに合併補正されていて、廃止されたコードは出ない。
- メッシュ = `M` + 地域メッシュコード
- 国 = ISO 3166-1 alpha-3

`region_level` が `prefecture`・`municipality` の dataset は、コードに対応する名称（`name_ja`）と、市区町村なら都道府県コード（`pref_code`、2 桁）を列に持つことがある。

### 5.6 latest.json

`latest.json` は、同じ dataset の `data.parquet` / `data.csv.gz` から選んだ行の部分集合で、データに無い値は持たない（latest ⊆ data）。選び方は `meta.latest` が宣言する。

- `meta.latest.select.partition_by`: 行をまとめる列（例: `region_code`）。空なら dataset 全体で 1 行。
- `meta.latest.select.order_by`: その中で最大の行を選ぶ列（時間軸の列）。最大の行が 2 行以上あれば公開されない。
- `meta.latest.items`: `values[]` の各要素が持つ列（名前・型・nullable）。`latest_schema_hash` はこの items から計算される。

`values[]` は partition ごとに 1 要素で、`partition_by` → `order_by` → 残りの列の順に並ぶ。値の表現は第 5.2 節の JSON 列。`latest` を宣言しない dataset には `latest.json` が無く、`meta.latest` と `latest_schema_hash` は null。

## 6. 表示の義務

dataset を表示・再配布するときは、`required_credits`（提供元固有の必須文言）を原文のまま表示し、続けて `attribution`（一般の出典表記）を表示する。どちらも `v1/catalog.json` の各 dataset にある文字列の配列で、同じ文言は `meta.json` の `sources[]` に source ごと（`required_credit`・`attribution_text`）にもある。どちらも文言の完全一致で重複を除き、`source_id` の昇順に並んでいる。`meta.indicators[].notes` は情報提供で、表示は任意。文言は要約・改変せずそのまま使う。

## 7. 取得と整合

- catalog に書かれた `sha256` と、取得した artifact が合わないときは、artifact を再取得する。それでも合わないなら catalog を再取得する。公開の切り替え中は、旧い catalog と新しいデータが一時的に混ざりうる（秒から分の単位）。
- 公開の順序は、データ → 利用条件・Schema・案内 → 最後に `v1/catalog.json` を置き換える。catalog が存在しないファイルを指すことは無い。
- `.csv.gz` は `Content-Encoding` を付けずに配信される。クライアントが自動で展開すると `sha256` が合わなくなるので、展開せずにバイト列で扱う。
- JSON・テキストは再検証（ETag）を前提にした配信設定で、データも `must-revalidate` で配信される。

### 7.1 変更の検知

catalog と meta のフィールドのうち、変更の検知に使うものの意味は次のとおり。

- `data_version`（catalog のトップと各 dataset）: データの元になった取得断面の日付（JST、`YYYY-MM-DD`）。新しい断面で公開されるたびに進み、同じ断面からの再公開では変わらない。
- `updated_at`（各 dataset）: その dataset の中身（列の宣言と全行）が最後に変わった `data_version`。`data_version` が進んでも中身が同じなら変わらない。**取り込み直す要否はこれで判断する。**
- `schema_hash`（各 dataset と `meta.dataset`）: 列の `name`・`type`・`nullable` から計算した値。取り込み時に保存し、次に読む前に比較する。変われば列の宣言が変わっている（additive な列の追加でも変わる）。
- `row_count`: 行数。
- `build_id`（catalog のトップ）: 公開の識別子。意味を解釈しない。
- `last_fetched_at`: 第 4 節 9。

`v1/catalog.json` は ETag 付きで配信されるので、`If-None-Match` で取得し、変わったときだけ各 dataset の `updated_at`・`schema_hash` を見る。

## 8. 利用条件

配布物の利用条件は `licenses/distribution_terms.md`、出典と提供元の規約の一覧は `licenses/index.json` にある。この文書は利用条件の本文を持たない。

## 9. 用語

| 用語 | 意味 |
|---|---|
| dataset | 公開の単位。`v1/{dataset_id}/latest/` の下のファイル一式 |
| artifact | dataset を構成する 1 ファイル（`data.parquet`・`data.csv.gz`・`meta.json`・`latest.json`） |
| consumer・利用側 | 公開ファイルを取得して使う側 |
| 公開（build） | 取得断面から全 dataset を一括で作り、catalog ごと置き換えること。`build_id` がその識別子 |
| paused | 更新を止めて前回のデータをそのまま配信している dataset（第 3 節） |
| 断面 | ある時点の取得データの固定された写し。`data_version` はその日付 |
| 出典側の規約 | 各提供元の利用規約（`licenses/index.json` の `terms_url`） |

改版履歴:

- 第 1.0 版（2026-10-23）: 初版。
