作成日:2026-10-21。状態:作成者側の設計草案。runtime・native 仕様・宣言・実行用 fixture・実データの実行は未実施。R4 の評価設計04 の D04(薬剤概念の版更新監査、一方向、SAB=RXNORM、独立確認者なし)を、P2 系列の契約として実装する設計です。利用者の指示(2026-10-21「CPC の版の監査に進みます」)と、範囲の確認(契約+架空の例+実データの実行、属性は宣言した方針で分類し未宣言は依頼)に従います。独立確認は未実施で、independent_review / evaluation_frozen は false のままです。実データの取り扱いは D03(03-data-access.md)と台帳(03-materials.json)に従い、生データ・記録の中身は Git に入れず、文書には集計と方法だけを残します。committed な例はすべて架空です。
1. 問い
| ID | 問い |
|---|---|
| Q1 | 月次更新のとき、既存の参照(旧版の概念 ID)ごとに、「新版でそのまま使えるか/何が変わったか/判断に追加情報が要るか」を、比較した範囲を明示して報告できるか |
| Q2 | 同じ ID が続くこと(継続)と、意味が同じであることを区別できるか。新版に ID がないとき、後継を名称の一致などで推測せず、不明と報告できるか |
| Q3 | 名称・関係・属性の差を別の面として比べ、どの面にも差がなかったときだけ「比較した範囲では差なし」と言えるか。比較していない面を差なしに数えないか |
| Q4 | 属性の重要度(定義にあたるのか、管理上の情報か)をライブラリが決めずに、宣言を受けて使い、未宣言の属性の差は推測せず依頼にできるか |
| Q5 | この契約を、実際の 2 版(81,468 概念)に適用して、面ごとの変化の量と、宣言の違い(方針の感度)を測れるか |
2. 下調べで分かっていること(設計の前に見た集計)
設計の前に、取得済みの 2 版(SAB=RXNORM)の差を、面ごとの件数だけで集計した。以下は予測ではなく、既知の値として扱い、予測(§8)は、まだ計算していない量と、契約の性質に限る。
| 面 | 既知の集計(9 月 → 10 月) |
|---|---|
| 概念 | 9 月 81,468、10 月 81,620。両版にある 81,298、9 月だけ 170、10 月だけ 322 |
| 原子 | 両版にある原子 140,535 件は、用語種別・文字列・抑制の欄が 1 件も変わっていない。変わるのは追加(539)と削除(306)だけ。続く概念で原子が増減したのは 3 件(-2、+1) |
| 概念間の関係 | 追加 1,932、削除 1,388。すべて追加・削除された概念につながる。両端が両版にある概念どうしの関係の変化は 0 |
| 属性 | 追加 2,521、削除 2,519((RXCUI, 属性名, 値) の集合)。続く概念に対する追加 1,723、削除 1,887。大半は NDC(削除 1,844、追加 1,751)。強度に関わる属性(RXN_AVAILABLE_STRENGTH、RXN_STRENGTH、RXN_BOSS_* など)も一部変わる |
| 構造 | SAB=RXNORM の RXNSAT は全行が原子(AUI)に付く。SUPPRESS は概念・関係・属性のすべてで空または N。RXNREL は概念対概念 553,500 行と、原子対原子 59,472 行 |
| 関係の向き | has_ingredient・isa・consists_of・has_dose_form・tradename_of の用語種別の組(例:RXCUI1 が成分 IN、RXCUI2 が薬剤成分 SCDC)から、行は「RXCUI2 が RXCUI1 に対して RELA の関係にある」と読む向きに合う(NLM の説明と同じ。NLM の文書自体は未取得で、局所データの集計による裏付け) |
この集計は、概念の定義にあたる部分がほぼ動かず、動いているのは「収載の出入り」と「属性の入れ替わり」だと示す。ただし意味が保たれたこと、廃止・置換が起きたこと、NDC の入れ替わりが定義の変化でないことは、この集計では示せない。
3. 設計判断
| 判断 | 初回に採る設計 | 理由・見直す条件 |
|---|---|---|
| UD1 | 単独の専用 R5を新設する(P2 系列の他の拡張に依存しない)。操作 run_terminology_audit:旧版の snapshot と新版の snapshot、比較範囲(出典)、属性の方針、監査する参照の列を受け、参照ごとの監査の行と、集計を返す。一方向(旧→新)。逆向きの成立を推定しない |
D04 の一方向。クラス R5:保存を主張しない診断(変わる点を記録、認定を出さない、臨床の主張を足さない) |
| UD2 | snapshot は正規化した形:concepts[]={id, source, atoms[{id, kind, name, attributes[{name, value}]}], relations[{kind, target}]}。概念の同一性は (出典, id)。関係はその概念から出る向きで、向きの読み替えは呼び出し側(正規化)の責任。契約は向きを反転しない。属性は原子に付く。名前・属性名・値・種類は不透明な文字列で、内容を解釈しない |
実データの構造(原子に付く属性、行の向き)に合わせる。不透明にして、第三者の語彙の内容に触れない |
| UD3 | 継続=新版に同じ (出典, id) の概念がある。名称・属性・関係の一致は継続の判定に使わない。新版にないときは not_found、successor: not_determined で、後継の候補を一つも返さない。追加情報の依頼(provide_official_history)を返す |
名称の一致だけで概念を同一視しない(D03 の誤った扱い)。CPC は廃止・抑制された内容を含まないので、不在は廃止の証拠でない |
| UD4 | 比較する面は三つ:名称(原子の追加・削除・同じ id の種類/文字列の変更)、関係((種類, 対象) の集合の差。対象が各版に存在するかの旗つき)、属性(両版にある原子の (原子, 名前) ごとの値の集合の差)。面を混ぜた推論をしない。追加・削除された原子に付く属性は、属性の差でなく、名称の差に伴う件数(with_atom_change)として数える |
P-CPC-2 の期待(名称の変更を含量の変更と推定しない)。原子の増減と属性の増減を分ける |
| UD5 | 属性の方針は呼び出し側の宣言:policy.attributes[]={name, class}、class=definitional(定義にあたる)/administrative(管理上の情報)。ライブラリは属性名の意味を知らない。差のあった属性が未宣言なら、差を undeclared に分けて依頼(declare_attribute_class)にし、推測しない。使った宣言(差のあった属性)は assumptions に残す。使われなかった宣言は痕跡を残さない |
P2-q の status・code と同じ規則。専門的な判断を実装が決めない |
| UD6 | 継続した参照の state(優先順):changed(名称・関係の差、または定義にあたる属性の差がある)→needs_information(未宣言の属性の差がある)→changed_administrative_only(管理上の属性の差だけ)→unchanged_in_scope(比較した三つの面のどれにも差がない)。not_found・not_compared(出典が比較範囲の外)はそのまま。unchanged_in_scope は「比較した範囲で差なし」で、意味の同一ではない |
比較不能を差なしとして扱わない |
| UD7 | 関係の対象が新版に存在しない(未解決)ときは unresolved_in_to={target, in_from} に出す。旧版に存在した対象が新版で未解決なら関係の面は changed、旧版でも存在しなければ既存の状態として報告だけ |
依存の欠落を見逃さず、既存の不整合を差分に数えない |
| UD8 | 出力は参照ごとの行(presence・state・facets・requests・assumptions・successor)、比較範囲(出典・面・方針・比較していないもの)、集計(概念数・継続・旧のみ・新のみ・状態別の数)。認定は常に not_certified、推測で補った情報は常に空 |
P2-q 以降と同じ |
| UD9 | 料金:M=1+n_from+n_to+Σ(参照ごとの料金)+1。参照の料金は、比較範囲の外なら1、範囲内なら 1+A_f+T_f+R_f(旧版の原子数・属性数・関係数)に、新版にあるときだけ A_t+T_t+R_t(新版の同じ数)を足す。n は snapshot の概念数。入力不正は予算に依らない。未完了の行は値の欄を null にし、集計も null | 手で導ける式 |
| UD10 | 実データの実行は契約の関数を直接呼ぶ(JSON の大きさ・予算の限界を通らない)。実データの snapshot は、道具(scripts/research/audit-cpc-versions.py)が台帳のハッシュを確かめた ZIP から正規化して作る。正規化の写像(列の位置、行の向き、重複の除去、原子対原子の関係・原子に結べない属性の扱い)は、架空の RRF 形式の ZIP を使うテストで確かめる。実データの結果(行を含む)は Git 管理外の data/research/r4/ に置き、文書には集計だけを書く |
D03 の「生データを共有しない」 |
4. 入口と型(予約名)
| 項目 | 予約する型・範囲 |
|---|---|
| profile / operation | p2-terminology-audit-draft-1 / run_terminology_audit |
| request / result | p2-audit-request-draft-1 / p2-audit-result-draft-1 |
| 回答型 | terminology_version_audit |
| 要求 | schema_version, operation, consumer, from, to, scope, policy, references, budget |
| from・to | {ref, version, concepts[], source_sha256?}。ref が同じ(同じ用語)で version が異なること |
| scope | {sources[]}(比較する出典。最大8) |
| policy | {attributes[{name, class}]}(最大64) |
| references | 旧版の概念 ID の列(最大256、重複なし、旧版に存在すること) |
結果の行(results[]、参照の昇順):reference, source, presence, state, facets, requests, assumptions, successor, assessment_complete, reason_codes。presence=continues/not_found/not_compared。facets=継続の行だけ:names{state, added[], removed[], modified[]}、relations{state, added[], removed[], unresolved_in_to[]}、attributes{state, definitional[], administrative[], undeclared[], with_atom_change{added, removed}}。他は null。トップレベル:direction: from_to、reverse_inferred: false、versions、scope、policy、summary。
5. 判定の規則
- 参照の (出典, id) が旧版にあり、出典が
scope.sourcesにない→not_compared。 - 新版に同じ (出典, id) がない→
not_found(successor: not_determined、依頼provide_official_history)。 - あれば継続。三つの面を比べる:名称(原子 id をキーに、追加・削除・種類/文字列の変更)、関係(
(種類, 対象)の集合。対象が各版に存在するか、新版で未解決か)、属性(両版にある原子の(原子, 名前)ごとに、値の集合が異なるもの。差を方針でdefinitional/administrative/undeclaredに分ける)。 - UD6 の優先順で
state。未宣言の属性の差があれば、状態に依らず依頼を返す。
6. 七欄と義務の主題
| 欄 | 内容 |
|---|---|
| 前提 | 属性の分類は宣言(作り手の判断)。正規化(出典の行の読み方)は呼び出し側。契約は不透明な文字列を比べる |
| 版 | 自己の profile・仕様 hash を consumer が固定。用語の版は snapshot の version と任意の source_sha256 |
| 確認対象 | 確認は作らない。参照の継続と面ごとの差を報告する |
| 情報の選択 | 比較範囲(出典・面)を明示。範囲外は比較しない |
| 問い | 「この参照は、新版でそのまま使えるか。何が変わり、何が判断できないか」 |
| 保持・喪失・追加 | 差の報告(追加・削除・変更)と、比較していないもの(履歴・意味・他の出典・原子間の関係・抑制)を明示。使った宣言を仮定として残す |
| 資源・停止 | UD9。停止の順序:静的検査→索引→参照ごと→最後の確定 |
義務(16:共通4+R5の3+AU-1〜9):AU-1(A)自己の版と実体。AU-2(I)各参照がちょうど一つの状態に入る(分割)。AU-3(I)継続の同一性と、後継を決めないこと。AU-4(I)面を別に比べ、差なしの条件。AU-5(I)属性の方針と依頼。AU-6(I)関係の向き・対象の存在・未解決。AU-7(I)比較範囲と範囲外・集計。AU-8(F・E)料金・停止の順序・未完了行。AU-9(H)推測で補わない(後継・逆向き・意味)。予測(H2):主題の外の義務は出ない(この契約は外部資料の事実を native に持たないため。実データの写像の事実は道具とそのテストが持つ)。
7. 実データの実行(道具)
scripts/research/audit-cpc-versions.py:台帳のハッシュで 2 つの ZIP を確かめ、RXNCONSO・RXNREL・RXNSAT の SAB=RXNORM の行を正規化して契約の関数に渡す。ZIP の展開・SQL の実行・通信は行わない。
| 正規化の写像 | 内容 |
|---|---|
| 概念 | RXNCONSO の SAB=RXNORM の行の RXCUI(出典は RXNORM)。原子={id: RXAUI, kind: TTY, name: STR}。同じ原子は一つ |
| 関係 | RXNREL の SAB=RXNORM かつ STYPE1=STYPE2=CUI の行を、所有者=RXCUI2、種類=RELA(空なら REL)、対象=RXCUI1 の関係とする(局所データで向きを確認済み)。同じ組は一つ。原子対原子の行は比較しない(件数を報告) |
| 属性 | RXNSAT の SAB=RXNORM かつ STYPE=AUI の行を、原子(RXAUI)に付く {name: ATN, value: ATV} とする。原子に結べない行・重複は件数を報告 |
実データの属性の方針は、作り手の研究上の仮定(医学的判断ではない)として二通りを宣言する:P-full=NDC を administrative、データに現れる他の全ての属性名を definitional。P-partial=NDC を administrative、強度に関わる属性名だけを definitional、他は未宣言。方針の違いが状態の分布をどう変えるか(感度)を測る。
8. 実装前に固定する予測と反例
8.1 予測(契約の性質)
| ID | 予測 | 外れた場合に分かること |
|---|---|---|
| AP1 | 他の拡張・実装・仕様・宣言・旧実行例を一つも変えずに実装できる。変更は host.py の2行、手で持つ一覧(P2-q・P2-t と同じ11か所)、道具と台帳の追加だけ | 標準の登録の手間 |
| AP2 | 登録は25宣言・351義務・533防護例、独立な組は 232/276(新しい拡張は基底を除く他の23件と依存せず、23組が独立に増える) | 依存の宣言 |
| AP3 | 各参照がちょうど一つの状態に入る。状態別の数の和は参照の数に等しい。実データでも成り立つ | AU-2 |
| AP4 | 継続は (出典, id) だけで決まる。旧版の概念と同じ名称・属性・関係を持つ新しい ID の概念があっても、旧版の概念は not_found で、候補を返さない |
AU-3 |
| AP5 | 面は独立:ある面の差は他の面の結果を変えない。比較した三つの面に差がなければ unchanged_in_scope |
AU-4 |
| AP6 | 未宣言の属性の差は全て依頼になり、推測で分類されない。使われなかった宣言は痕跡を残さない | AU-5 |
| AP7 | 旧版と新版を入れ替えた監査は、継続した参照の各面の差を入れ替えたものになり(追加と削除、値の from と to)、not_found と新版のみの数が入れ替わる。結果は逆向きの推定を含まない(reverse_inferred: false) |
一方向 |
| AP8 | 料金 M の式が全要求で成り立つ。手で導いた値(例:2参照・全概念4・差なしで M=30)と一致する | UD9 |
| AP9 | 義務は R5 と必須欄で足りる。主題の外の義務は0 | H2 |
| AP10 | affected():この拡張は他の拡張に影響されず、他の拡張に影響しない(自分だけ) |
依存の効果 |
8.2 実データの予測(まだ計算していない量に限る)
| ID | 予測 | 備考 |
|---|---|---|
| RP1 | 全 81,468 参照が一つの状態に入る。not_found=170、新版のみ=322(既知の値の再現)。名称の差のある概念=2(既知)。原子の modified=0(既知) |
既知の値の再現の検査 |
| RP2 | 続く参照のうち、属性の差が一つ以上ある概念(P-full・原子単位)は 1,500〜3,300 | 追加 1,723・削除 1,887 の件数から |
| RP3 | そのうち NDC 以外の属性の差がある概念は 100〜600 |
NDC 以外の削除は約 675 件 |
| RP4 | 関係の差がある続く参照は 100〜1,300。除かれた関係の対象は、全て新版に存在しない(target_in_to が真の除去は0) |
両端が続く関係の変化は0(既知)から導かれる |
| RP5 | 続く参照で、新版で未解決かつ旧版に存在した対象(unresolved_in_to の in_from が真)は 0。旧版でも未解決の対象は少数 |
版内の整合 |
| RP6 | P-full での続く参照の changed は 300〜1,500、unchanged_in_scope は続く参照の 95% 以上(77,200 以上) |
面ごとの件数の和から |
| RP7 | P-partial では needs_information が 0 より大きく、changed の数は P-full と等しい(未宣言の属性の差だけの参照が、changed_administrative_only や unchanged_in_scope から needs_information に移るだけ) |
方針の感度。優先順 UD6 の帰結 |
8.3 反例(83 に具体値を固定)
| ID | 内容 | 試すこと |
|---|---|---|
| Z1 | 同じ ID で強度(定義にあたる属性)が変わる | 継続だが changed。ID の継続を意味の同一としない |
| Z2 | 管理上の属性(NDC など)だけが変わる | changed_administrative_only。changed に混ぜない |
| Z3 | 未宣言の属性が変わる | needs_information、依頼。分類を推測しない |
| Z4 | 旧版の概念が消え、同じ名称・属性・関係の新しい ID の概念が現れる | not_found、successor: not_determined、候補なし |
| Z5 | 名称の一部(同義語の原子)だけが変わる | changed(名称の面)。含量などの意味の変更と推定しない |
| Z6 | 関係の対象が新版から消える/関係だけが落ちる | 対象の存在の旗が区別する。除去と依存の欠落 |
| Z7 | 出典が比較範囲の外の参照 | not_compared。範囲外の差は無視し、差なしにも数えない |
| Z8 | 旧版と新版の入れ替え | AP7 |
| Z9 | 原子が減ると属性も減る | 属性の差でなく名称の差に伴う件数 |
| Z10 | 出典が変わった同じ ID | (出典, id) が違うので not_found(出典の変更は別の概念として扱う。限界として記録) |
| Z11 | 並びの違い(原子・関係・属性の順序) | 差にならない |
| Z12 | 同じ版の組/別の用語の組 | 静的に拒否(same_version/system_mismatch) |
9. 受入条件
- 期待・予測・反例を、runtime より先に別の文書(83)で固定し、実行結果から期待を更新しない。
- 専用 native・module・R5 宣言・fixture・独立な別計算・道具と、道具の正規化のテストを追加する。他の拡張は変更しない(AP1)。
- 別計算は、集合の演算で独立に書く(native の面ごとの差の計算を読まない)。
- 全数(属性の方針の部分集合×差の種類)、乱数、入れ替え、並びの不変性、料金の式、予算の掃引、入力の不変性、型破損、変異(継続を名称で決める、後継の候補を返す、方針の推測、面の混合、範囲外の比較、優先順の取り違え、先払いの省略)を検査する。
- 実データの実行を、方針二つで行い、集計(既知の再現、予測の結果、感度)を結果の文書に記録する。実行は 2 つの ZIP のハッシュと台帳の一致を確かめてから行う。
- Python 3.11/3.14 で、全テスト系列、版比較つき統合評価、旧い22,052件の挙動 digest を回帰する。分母の変更は旧い値を履歴として残す。
- 追加前は
8c6d600、固定対象1,827ファイル、manifest94d5eabe7f4282c129e1fda3d407b35d7d5ca2d25006a2b95eecfc61e9a634e8、束e2cd592909c79f76d3e251d1728c88e9547f17636f80e3478b8ef9aa9c9b591b。件数・hash・予測の結果・残る穴を結果の文書に残す。independent_review/evaluation_frozenは false。
10. 範囲外
後継・置換先の決定、廃止と収載条件の変化の区別、公式の履歴・対応表の利用、成分・含量・剤形の意味の解釈、治療上の代替、原子対原子の関係、SUPPRESS・CVF の比較、出典 MTHSPL など RXNORM 以外、週次更新、全体版、逆向きの監査の導出、未見の月次版への適用(設計の固定後に別途)、第三者の語彙の内容。
この設計は、版更新の監査の契約を、実際の 2 版に当てて面ごとの変化の量を測る一つの実演です。参照の更新を安全に行えることの証明でも、薬剤の意味が保たれることの主張でも、他の月に変化の量が一般化することの証明でもありません。最終目標は変化に耐える医療コード体系そのものの確立であり、この有限の設計はその一つの検査です。
11. 利用者に確認すること
| ID | 確認 | 推奨 |
|---|---|---|
| U1 | NLM の技術文書(関係の向き)と CPC の説明(収載の範囲)のページを、台帳のハッシュつきで取得するか。この設計では取得せず、向きは局所データの集計で裏付けた | 取得しない(希望があれば取得して事実の表に結ぶ) |
| U2 | 実データの属性の方針(P-full、P-partial)は作り手の研究上の仮定で、医学的判断ではない | そのとおり |
利用者の指示は「進みます」だったため、推奨どおりに実装へ進みます。