Kairos 言語仕様 — 付録 B. 供給側プロファイル(表を配る側の契約)
本書は仕様ツリーに置く付録で、規範(§1〜7)の一部ではない。付録 A(発報層プロファイル)が
「点列を実行する側」の契約なら、本書は「表を配る側」の契約である。external(§3.8・
ADR-46)が実行時に受け取る解決値を、誰が作っても同じ形・同じ検査で届くように定める。
英訳は en/spec/61。
B.0 この文書は何か
言語は表の形と検査を持つ。表そのものは配らない。年で腐るデータ(祝日・節気・朔)は、公示する機関を上流に持つ供給側が配る。 本書は供給側に「これを満たせば言語の検査をそのまま通る」を示す。
適合の段は付録 A と同じ 3 つ。
- MUST=満たさなければ検査に落ちるか、利用側の信号が壊れる。
- SHOULD=推奨。外すなら理由を記す。
- MAY=任意。
本書の立場。
- 供給側が作るのは静的束(supply JSON)だけ。言語側は何も変えない。
- 検査できるものは評価器が検査する(形・整列・包含・実在日・ラベルの域)。検査できないもの(covering の真実性・asof の正しさ・出所の正しさ)は供給側の責務として本書に書く。
- 配信の手段(HTTP・ファイル・DB)は言語の外(ADR-15)。本書は手段を問わず、URL で配る形を例に書く。
- 製品名・組織名は書かない。誰が配っても同じ契約。
読む順は §3.8 → reference/external.md → 本書 → 付録 A の A.5。
- 形の正本:
schema/supply.schema.json。 - CLI の節:
impl/README.md(--supply・--tsv・--json)。 - 判断の根拠: ADR-37・ADR-45・ADR-46。
- 線引きの出所: 維持と継承(「年で腐るデータを言語は配らない」)。
B.1 配信物の形(静的束)
1 ファイルが 1 つの束。束縛名をキーに、表を 1 つずつ入れる。
- MUST: 形は
{ "<束縛名>": { … } }。キーは束縛名かpremise.束縛名(修飾名が優先)。 - MUST: 各表の必須は
covering・asof・そしてdatesかinstantsのどちらか一方。 - MUST:
datesは"YYYY-MM-DD"の列。premise のtz:の市民日で、昇順・重複なし・実在する日。 -
MUST:
instantsは epoch ミリ秒の整数列。昇順・±8.64e15 以内。 - MUST:
labelsは点と同じ長さの並行列で、要素は文字列。宣言側のlabels: [...]の値域の中だけ(宣言のlabels:は識別子なので、数値や真偽値は受け付けない)。宣言が無い束縛にラベルを送らない。 - SHOULD: 1 表 1 ファイル。束縛名が分かるファイル名にする(
jp-holidays.jsonにholidays)。 - MAY: 1 ファイルに複数の表(同じ出所・同じ更新周期のものだけ)。
- MAY: 時刻を持つ表(節気・朔)は、
datesの表とinstantsの表を別の束縛名・別のファイルで両方出す(sekkiとsekkiAtなど)。利用側はinstantsから|> snapTo(day)で日に落とせるので、instantsが一般形でdatesが便利な形。
祝日表の例。
{ "holidays": {
"dates": ["2026-01-01", "2026-01-12", "…", "2027-11-23"],
"covering": "2026-01-01..2027-12-31",
"asof": "2026-10-22" } }
ラベル付きの例(節気)。宣言側は sekki = external(kind: dates, labels: [小寒, 大寒, 立春, …], source: "…")。
{ "sekki": {
"dates": ["2026-01-05", "2026-01-20", "2026-02-04"],
"labels": ["小寒", "大寒", "立春"],
"covering": "2026-01-01..2026-02-28",
"asof": "2026-10-22" } }
- 註:
datesの字面を日付に錨打ちするのは言語側。供給側は tz を計算しない(利用側は派生のtz:上書きで再錨できる)。 - 註:
instantsに壁時計の字面を使わないのは、DST の重複で二意になるため(ADR-46 判断 8)。
B.2 covering=完全性の主張
covering は「この範囲は網羅した」の主張である。範囲内に無い日は「無い」と読まれる。範囲外は「未知」と読まれ、利用側に註釈が出る。
- MUST: 点はすべて
coveringの中。外の点は契約違反。 - MUST: 公示済みの範囲だけを
coveringに入れる。未公示の年を含めない。 -
MUST:
coveringを広げて「未知」を「無い」に見せない。広すぎる covering は、利用側の範囲外の註釈と残走路を黙らせる。 - SHOULD: 年の境で切る(
2026-01-01..2027-12-31)。年略記2026..2027も同じ意味。 - SHOULD: 開端(
..・2026-01-01..)は祝日・節気の表に使わない。閉じられる区間だけを運ぶ。 - SHOULD: 複数区間(
,区切り)は欠けた年がある表にだけ使う。 -
SHOULD: 月で閉じる表(朔と月番号)は年の境で切らず、前年の最後の朔から、最後の閉じた月の末日までを covering にする。次の朔と月番号を決める中気が未公示の月(年末の月)は載せない。推定の月番号は配らない(「公示済みの範囲だけ」の MUST が年の境より優先する)。
- 註: 「まだ何も無い」は
dates: []+coveringで表す。正当な値で、評価は成功し点は 0 になる(ADR-45)。解決の失敗とは型が違う。 - 註: 残走路=評価範囲の
toから covering の末尾までの日数。利用側はこれを閾値監視に配線する(付録 A の A.4)。供給の更新が遅れると、ここが負に転じて見える。
B.3 asof=版の識別子
asof は版の名前である。同一性だけが比べられる。順序も日付の意味も読まれない。
- MUST: 非空の文字列。版を切る識別子が無ければ観測日
YYYY-MM-DD。 - MUST: 中身を変えたら
asofも変える。同じasofで中身の違う束を 2 つ作らない。 -
MUST: 中身を変えないなら
asofも変えない。配信後にasofだけが変わると、利用側では別の配信として扱われうる(付録 A の A.3・A.5)。 - 註: premise の静的
asof:(版ピン)と解決値のasofが違うと、被覆サマリに「宣言 asof … と不一致」が常時表示される。黙って上書きされない。 - 註: 同じ名前の表に、読み手によって違う中身を配らない。無料と有料、鍵の内と外で中身が違うなら別の名前にする。同じ
asofで中身の違う束が 2 つできるのを防ぐため。
B.4 URL と版
配る場所は固定し、配った版は消さない。
- MUST: 最新の固定 URL を 1 表に 1 つ(例:
https://<配信元>/supply/jp-holidays.json)。 - MUST: 版ごとの URL(例:
https://<配信元>/supply/jp-holidays/2026-10-22.json)を残す。更新しても旧版の URL を消さない。再現と監査のため。 -
SHOULD: 最新は版への別名にする。中身を 2 か所で持たない。
- SHOULD: 変更は追加だけ。配った年の行を変えない。公示の訂正だけが例外で、訂正日を新しい
asofにして記録に残す。過去の年を落として「当年だけ」に入れ替える形は、この約束と B.6 (4) の削除 0 に反する。残す。 - SHOULD:
Content-Type: application/json・CORS 許可・キャッシュは短く(1 日程度)。 - SHOULD: 同じ場所に
index.json(表の一覧・出所の URL・最終更新・次回更新の予定)と、人が読む 1 ページ(出所・更新の約束・版の URL の規則)。
B.5 更新の作法
表は年に 1 度延びる。延ばす約束を公開し、延ばしたら版を切る。
- MUST: 出所の公示から何日以内に延ばすかを公開する(日数は供給側が決める)。
- MUST: 延ばすときは
coveringを延ばし、asofを更新日にする。点の追加とcoveringの延長は同じ版で行う。 - SHOULD: 延ばしたら購読者に知らせる(メール・RSS・
index.jsonの更新日)。 - SHOULD: 延ばす前に B.6 の受け入れ試験を走らせ、記録を残す。
- SHOULD: 人が読む 1 ページに、出所の表記(国立天文台が求める「(C) 国立天文台」等)と利用条件の所在を書く。
出所の例(日本)。表の権威は公示する機関にある。言語も供給側もそれを置き換えない。
| 表 | 出所 | 公示の時期 |
|---|---|---|
| 祝日 | 内閣府「国民の祝日について」 | 前年の 2 月上旬 |
| 二十四節気・朔 | 国立天文台 暦要項 | 前年の 2 月 1 日(官報) |
B.6 受け入れ試験
言語側の配布物(npx kairos-lang・schema/supply.schema.json)だけで通る 4 本。配信の前と、延ばすたびに走らせる。
以下、新版を s.json(covering 2026〜2027 年・asof 2026-10-22)、旧版を old.json(covering 2026 年だけ)、宣言を check.kairos と呼ぶ。
(1) 形: schema/supply.schema.json に通る。評価時に CLI が形を検査するので、事前検査は任意の JSON Schema 検証器で。
(2) 契約: 評価器の検査を通る。宣言と表を組み、覆域の全体を評価する。
premise JP { calendar-system: Gregorian; tz: "Asia/Tokyo"; wkst: Mon
holidays = external(kind: dates, source: "https://example.com/supply/jp-holidays.json") }
@JP
holidays
npx -y kairos-lang list --supply s.json --from 2026-01-01 --to 2028-01-01 --tz Asia/Tokyo check.kairos
期待: 終了コード 0。点が公示どおり。⚠ 範囲外 が出ない。末尾の被覆サマリが
JP.holidays covering 2026-01-01..2027-12-31 asof 2026-10-22 残走路 0 日。
(3) 表の外は黙らない: covering の外を聞くと註釈が出る。出なければ covering が広すぎる。
npx -y kairos-lang list --supply s.json --from 2028-01-01 --to 2028-02-01 --tz Asia/Tokyo check.kairos
期待: 終了コード 0。# 0 点・# ⚠ 範囲外 2028-01-01..2028-02-01(JP.holidays covering 2026-01-01..2027-12-31, asof 2026-10-22)・残走路 -31 日。
(4) 延長は追加だけ: 旧版と新版の TSV の差分が追加だけで、削除が 0。
npx -y kairos-lang list --supply old.json --from 2026-01-01 --to 2028-01-01 --tz Asia/Tokyo --tsv check.kairos > old.tsv
npx -y kairos-lang list --supply s.json --from 2026-01-01 --to 2028-01-01 --tz Asia/Tokyo --tsv check.kairos > new.tsv
diff old.tsv new.tsv | grep -c '^<' # 期待: 0
- 註: 旧版の評価では
⚠ 範囲外 2027-01-01..2028-01-01と負の残走路(-365 日)が出る。これが「延ばす前」の正常な姿。 - 註: (4) が意味を持つのは延長の前後の比較。同じ範囲の版どうしでは追加 0・削除 0 になるだけで、約束の検査にはならない。
- 註: ラベル付きの表は
--tsvのlabel列(1.0.12)で照合する。 - 註: 契約違反は終了コード 1。
--jsonではerror.kindがsupply。
契約違反の例(どれも終了コード 1)。
| 違反 | 診断(冒頭) |
|---|---|
| 乱順・重複 | 契約違反: 解決値は昇順・重複なし |
| 点が covering の外 | 契約違反: 解決値の点が covering の外 |
| 実在しない日 | 契約違反: 実在しない日付 |
| kind 不一致 | 契約違反: kind: dates の解決値は dates("YYYY-MM-DD" の列)を運ぶ |
| ラベルが宣言の値域の外 | 契約違反: 宣言値域の外のラベル |
| ラベルの長さ違い | 契約違反: labels は時点列と同長 |
| 宣言の無い束縛にラベル | 契約違反: labels 無宣言の external にラベルが来た |
covering・asof の欠落 |
--supply s.json の "holidays": covering(文字列)が必須 |
B.7 言語側が出すもの・出さないもの
| 言語側が出す | 言語側が出さない |
|---|---|
| 形の正本(schema)と評価器の検査 | 表そのもの(年で腐るデータ) |
| 本書と受け入れ試験の手順 | 配信基盤・URL・キャッシュ |
同梱例(impl/examples/)=照合用の点列 |
更新の約束・通知・課金・利用者管理 |
検査できないものは供給側の責務。
coveringの真実性=本当にその範囲を網羅して取得したか。asofの正しさ=中身が変わったときだけ変えているか。- 出所の正しさ=公示する機関の値を写しているか。
B.8 適合の自己申告(チェックリスト)
供給側が「Kairos の供給契約に適合」と言うときに添える一覧。MUST は全部に印を付け、SHOULD は外した項目と理由を書く。
MUST(形)
- 束は
{束縛名: {dates|instants, covering, asof [, labels]}}の形で、schema に通る(B.1) datesは premise の tz の市民日・昇順・重複なし・実在日。instantsは epoch ms・昇順(B.1)labelsは点と同長の文字列で、宣言した値域の中。宣言の無い束縛に送らない(B.1)- 点はすべて
coveringの中(B.2)
MUST(運用)
coveringは公示済みの範囲だけ。未公示の年を含めない(B.2)asofは非空の文字列。中身が変わったときだけ変える(B.3)- 最新の固定 URL と、消えない版の URL(B.4)
- 延ばす約束(公示から何日以内)を公開し、延ばすときは covering と asof を同じ版で更新する(B.5)
- 受け入れ試験 (1)〜(4) を配信前と延長のたびに走らせ、記録を残す(B.6)
SHOULD
- 1 表 1 ファイル。年の境で切る。開端を使わない。
- 変更は追加だけ。訂正は新しい asof。
index.jsonと人が読む 1 ページ。購読者への通知。
B.9 用語
| 用語 | 定義 | 参照 |
|---|---|---|
| 供給側 | external の解決値(表)を作って配る側。公示する機関の値を写す |
B.0 |
| 静的束 | 束縛名ごとに dates/instants・covering・asof・labels を運ぶ JSON。1 回の評価で 1 回解決される | B.1・A.5 |
| covering | 完全性の主張。範囲内に無い点は「無い」、範囲外は「未知」 | B.2・§3.8 |
| asof | 版の識別子。同一性比較のみ | B.3・§3.8 |
| 残走路 | 評価範囲の to から covering の末尾までの日数。負なら覆域切れ |
B.2・§4.10 |
| 範囲外の註釈 | covering の外を評価したときに結果へ並走する註釈 | B.2・§4.10 |
| 契約違反 | 解決値が形・整列・包含・実在・ラベルの検査に落ちた状態。終了コード 1・error.kind は supply |
B.6・ADR-46 |
| 供給エラー | 解決そのものの失敗(解決子が無い・取得できない)。error.kind は契約違反と同じ supply |
A.4・ADR-46 |
| 正当な空 | dates: []+covering。評価は成功し点は 0 |
B.2・ADR-45 |