実施日:2026-10-19。契約版:p0-draft-2。状態:作成者(AI-001)の単独・内部実施の結果。独立確認前。P2-a の結果 21 の「P2-b の前に判断すること」3 点を、利用者の指示(「拡張ホストを分離する」「防護例のない義務のうち拒否例を作れるものを作る」「宣言を固定対象に加える」)で実施した記録です。実装の挙動と契約(p0-draft-2)は変えていません。
| 判断 | 実施した内容 | 結果の要約 |
|---|---|---|
| 拡張ホストを分離する | handover.py(1 モジュールに全操作)を、ホストと操作に分け、最上位の振り分けを登録表にした |
分離の前後で、22,052 件の出力が一致。1 操作の変更が列挙する拡張が 6 件から 1〜2 件に |
| 防護例のない義務に拒否例を作る | examples/p2/ に防護例 35 件を追加し、24 件の欠落のうち 21 件を置き換えた |
欠落は 3 件(拒否する入力を構成できないもの)。実装のゲートを壊した 27 件の変異のすべてを防護例が検出(うち 10 件は既存テストが見逃した) |
| 宣言を固定対象に加える | 宣言と、宣言が根拠として引く実体を、共通 manifest(lock)に加えた | 固定対象 170 → 248 ファイル(宣言 11、宣言が引く防護例・検査・オラクルなど 62、新しいモジュール 5)。例外は監査スクリプト 1 件 |
1. 拡張ホストの分離
変更
| 変更前 | 変更後 |
|---|---|
handover.py(371 行)に、ヘッダーの検査・実体の確認・鎖・改名・欠落の診断・再生の点検・登録の振り分け |
handover.py(75 行)はヘッダーの検査・実体の確認・登録表(OPERATIONS)による振り分けだけ |
| - | handover_common.py(25 行):P0 の固定値、鎖の項目、操作の登録の形(Operation)、P0 要求の組み立て |
| - | chain.py(鎖と改名)、absence.py(欠落の診断)、replay.py(再生の点検):それぞれ登録(Operation)を持つ |
reference.py・projection.py は handover.py から呼ばれる |
同じく自分の Operation を持ち、ホストの登録表に載る |
__init__.py に evaluate(3 つの族への振り分けとP0 の既定)と多数の再 export |
host.py:SURFACES(schema_version → 処理・結果の schema・profile)と evaluate。__init__.py は evaluate だけを再 export |
関数の本体は、handover.py の行範囲をそのまま移した(転記による誤りを避けるため、手で書き直していない)。ヘッダーの検査は、操作ごとの項目と P0 の確認の要否を、各操作の Operation から読むようにしただけで、検査の順序・エラーの code・path は変えていない。
行わなかったこと:予測・事象・引き継ぎの 3 つの族のヘッダー検査の共通化。エラーの path や code が族ごとに違う(予算の不正に path が付くか、native 仕様の不一致の path)ので、1 つにまとめると結果が変わる。それはリファクタリングではなく契約の変更になるため、今回の範囲外とした。
挙動が変わらないことの確認
| 検査 | 結果 |
|---|---|
| 全例の出力・改変入力・予算の全値掃引(固定の seed。131 例×(1+改変 150)+予算の掃引)の出力 | 22,052 件が分離の前後で完全に一致(certifier だけは、実装のファイルの束の hash なので除いて比較)。出力の種類は 95(status と最初の理由コードの組)、例外 0 |
| 既存の開発テスト(231)・追加監査(8) | 無変更で通過 |
| レビュー系列(52) | 1 箇所だけ変更:差し替え先を med_dsl.handover._compare から med_dsl.chain._compare へ |
| P2 系列 | 無効化の方法を変更(§下) |
| P1 の 20 条件・80 JSON・417 必須パス/P0 221 必須パス | 通過(lock を再固定) |
比較の仕組みは、全例を評価した結果を正規化して hash にし、ファイルとして保存するだけの補助(作業記録 refactor_digest.py)。分離の前に基準を取り、分離の後に同じ入力で比べた。入力が同じであること(seed を例ごとの名前から決める)と、基準が再現すること(2 回の実行で同一)は確認した。
再検証範囲の精度
affected() に、登録の辺の規則を入れた(実行契約 20 の §5)。ホストは登録のために操作のモジュールを import するが、これは依存の向きの逆で、辿るとどの操作の変更も全操作を列挙する。
| 変更するモジュール | P2-a の最初の実装(handover.py 1 モジュール) |
分離後 |
|---|---|---|
absence.py、replay.py、projection.py、reference.py |
(同じモジュール内)6 件 | 1 件(その拡張) |
chain.py |
6 件 | 2 件(鎖と、鎖の認定を読む再生) |
handover.py、handover_common.py |
6 件 | 6 件(ホストと全操作。ホストの変更は全操作に及ぶ) |
events.py |
7 件 | 7 件(事象化と、それに依存する引き継ぎの全体) |
host.py、__init__.py |
10 件 | 10 件(共有の入口の pin) |
forecast.py |
1 件 | 1 件 |
非干渉(X03)の無効化の方法
登録表が関数の参照を持つようになったので、モジュールの属性を差し替えるだけでは無効にならない。拡張を不活性にする方法を、宣言した実装モジュールが定義する全関数を、他のモジュールが import した束縛も含めて差し替え、ホストの登録表(host.SURFACES、handover.OPERATIONS)の該当の項目も差し替える形に変えた。これは、以前の「拡張ごとに差し替える関数の表」より厳しい:宣言したモジュールの関数のうち、宣言に現れない依存先から呼ばれるものがあれば、非干渉の破れとして見つかる。結果は以前と同じ(8 拡張を無効にしたとき、その拡張と依存先に属さない例の結果は変わらず、属する例は変わる)。宣言の誤り(p1-replay が鎖を読むことの書き落とし)が検出されることも、新しい方法で再確認した。
限界
- 登録の辺を辿らないので、ホストが操作の関数を実際に使う逆向きの読み取りは、列挙では見えない。非干渉の検査(無効化)が見るが、ホスト自身が持つ例がなければ見えない。
__init__.pyが再 export していた名前(PROFILE、Problemなど)を削った。リポジトリ内に使う箇所はなかった(使われていたのはevaluateだけ)が、外部の利用者がいればここは契約の変更になる。examples/review/のgate(例:handover.py:176)の行番号は分離前の位置。固定した例を書き換えないため、対応表を examples/review/README.md に追記した。
2. 防護例の追加
対象
P2-a で防護例のなかった義務 24 件のうち、拒否・境界の入力を構成できる 21 件に防護例を付けた。新しい例 35 件を examples/p2/ に置き(台帳つき。別の分母)、事象化の R3-4 だけは既存の例(p1-b/shared-key:同じキーの 2 つの主張が別々に保持される)を引いた。
| 義務の種類 | 付けた防護例 |
|---|---|
予算の境界(K-RES、8 拡張) |
各拡張の既存の例を、予算だけ変えた対:完了に要る最小の steps n の n−1 で止まる/n で完了する(上限を厳しくしすぎない)。参考意味には式の深さの上限(16)の境界も |
停止(K-STOP) |
定義:予算 0(追加が空でも)/鎖:未知の能力で認定しない/ホスト:未登録の操作・consumer の操作・profile/参考意味:有限領域の探査は未対応・式の深さの上限 |
不変条件(K-INV) |
定義:台帳を受け付けない/鎖:確認のない AI 候補が支持にならない(未決のまま両計算が一致)/ホスト:打切りで認定票・部分の報告を出さない |
鎖の範囲(COMP-2) |
範囲外の問い(事象の同一性)を旧の問いの認定に含めない |
予測(R2-2) |
任意の package・報告専用の述語以外を受け付けない |
参考意味(R3-4、R3-5、R3-6) |
新理論が文を一つ追加するとき被覆を真にも偽にもしない/台帳を受け付けない/宣言した語彙にない述語を持ち込めない |
欠落として残る 3 件(拒否する入力を構成できない。理由は宣言の guard_gap):定義の R1-3(旧モデルの拡張は構文の十分条件 R1-1・R1-2・R1-4 で保証され、独自の拒否例がない)、事象化の R3-3(被覆は手作業の導出で、反例の検査は参考意味が担う)、鎖の COMP-3(この鎖に損失のある段がない)。
作り方
期待結果は、ゲートの意味から手で指定し、処理系の出力から生成していない(例外は予算の境界の n で、無制限の実行の steps_used を測った)。鎖の入力は、変更した履歴から端点・範囲の宣言を作り直す必要があるため、実装の事象化・改名・端点の計算を入力の構成にだけ使った。35 件のうち最初の 33 件は、最初の実行で期待が全件一致した。一致しただけでは検出力の証拠にならないので、次の変異検査を行った(残りの 2 件は変異検査の結果で追加した)。
検出力(変異検査)
防護例を付けたゲートごとに実装を壊し(27 件)、意図した例が失敗するかを調べた。あわせて、既存の開発テスト(231)とレビュー系列(52)が同じ変異を検出するかも記録した。変異は、予算の課金の削除(9)、ゲートの条件の緩和・削除(11)、認定・採用の条件の変更(4)、予算の定義の変更(2)、停止時の応答の形(1)で、作成者が防護例の対象に合わせて選んだもの(網羅的な変異演算子の集合ではない)。P0 実行体のファイルを変異させるときは、変異後の hash に再固定した。対照(変異なし)は失敗 0。
| 結果 | 件数 |
|---|---|
| 意図した防護例が失敗した(検出) | 27 / 27 |
| 既存の開発テスト・レビュー系列も検出した | 17 |
| 既存のテストが見逃し、防護例だけが検出した | 10(欠落の診断・改名・射影・参考意味の課金、式の深さの下側の境界、有限探査の未対応、ホストの操作・consumer の操作・profile の検査、参考意味の語彙の検査) |
検査で分かったこと:
- 定義の初期の課金が、最初の例では守られていなかった。 「予算 0 は、追加が空でも未評価」という初期の課金(
definitions.pyの最初のbudget.tick())を外しても、追加が空でない例は後の課金で止まるので、予算 0 の例が失敗しなかった。追加が空の例(予算 0 と 1 の対)を足して検出できるようにした。 - 等価な変異が一つあった。 鎖の
certified = complete and not outsideのand not outsideを外しても、どの検査も失敗しない。範囲外の問いがあると事象化の比較の状態がpartialになり、complete(全比較がmatched)が偽になるので、not outsideは冗長(多重の防御)。代わりに範囲外の問いの集計(outside)を壊す変異で、chain-out-of-scope-queryが検出することを確認した。 - 重複した防御がある。 予算 0 を「無制限」とみなす変異(
Budget.tick)でも、鎖は止まる。内側の P0 の問い合わせ(query.py)が、予算 0 を未評価として止める独立のゲートを持つため。そのため鎖の予算 0 の例は、停止時の応答の形(incomplete: ["request"]、認定票なし)を壊す変異で検出できることを確認した。個々のゲートが単独で守られているとは限らない。 - 検査の環境の誤りを一度出した。 変異した作業ツリーの複製が git のリポジトリでなく、既存テストの一つが
git rev-parseで失敗するため、「既存テストで検出」の列が全変異で誤って検出になった。対照の変異を加えて気付き、環境を直して全変異をやり直した。結果は上の表の値。
限界
- 変異は作成者が選んだ 27 件で、防護例が検出できない退行が他にないことは示していない。
- 防護例の期待は作成者が手で指定した主張で、独立の確認は受けていない。
- 防護例のない義務 3 件の理由(「拒否する入力を構成できない」)は、構成できないことの証明ではなく、作成者が試みた範囲の判断。
3. 宣言の固定
固定したもの
共通 manifest(lock、examples/p1-evaluation/manifest.json)の対象に、次を加えた(評価器 evaluate-p1-d.py の EXTENSION_PATTERNS)。固定対象は 170 → 248 ファイル(新しい実装モジュール 5 を含む)。
| 追加 | ファイル数 |
|---|---|
extensions/*.json(宣言 10 と索引) |
11 |
examples/p2/*.json(防護例 35 と台帳) |
36 |
examples/review/*.json(宣言が引くレビュー系列の例と台帳) |
15 |
tests/review/*.py(宣言が引く検査と独立オラクル) |
7 |
tests/p2/*.py、tests/audits/*.py |
2、1 |
scripts/research/check-extensions.py |
1 |
実装モジュール(host.py、handover_common.py、chain.py、absence.py、replay.py) |
5 |
範囲の決め方:宣言だけを固定しても、宣言が根拠として引く防護例・検査・オラクルが差し替えられれば、宣言の主張は黙って崩れる。そこで「宣言と、宣言が引くものすべて」を固定対象にし、それをテストで保証した(test_the_lock_pins_the_declarations_and_everything_they_cite:実装・仕様・契約・pin・評価の例・検査・オラクル・防護例のうち、固定対象に載っていないものは、監査スクリプト 1 件だけ)。
例外:scripts/research/audit-preservation.py は載せない。固定対象の sha256 を定数に持つので、載せると自己参照の循環になる(test_the_audit_script_is_outside_the_lock_because_it_holds_the_lock_hash が、この理由が今も成り立つことを検査する)。固定対象の hash が変われば、固定対象に載る tests/audits が検出する。
分母は変えていない:P1 の 20 条件・80 JSON・417 必須パス、P0 の 22 条件・37 JSON・221 必須パスは同じ。レビュー系列と防護例は、宣言が根拠として引く実体として載るだけで、評価の分母(SUITES)には入らない。「レビュー系列は固定対象に載らない」という書き方だったテスト(tests/review/test_review_negatives.py)は、意図(分母に入らない)に合わせて「分母は 4 系列だけ」に直した。
効果と限界
- 宣言、防護例、検査、オラクルのどれを変えても、固定対象の再固定(明示的な
--create-snapshot)が要り、評価器のpreflightが不一致を拒否する。 - 固定対象は同じリポジトリのファイルなので、改ざんを防ぐものではなく、変更を見落とさないための関門。外側の錨は、固定対象の hash を持つ監査スクリプトの定数だけで、外部への記録(独立の確認者による固定)は未実施。
certifier(認定票に付く実装の束の hash)は、従来どおりsrc/med_dsl/の*.pyと*.jsonだけから計算する(24 ファイル)。宣言や例は含めない。
4. 検証
| 検査 | Python 3.14.4 | Python 3.11.17 |
|---|---|---|
| 開発テスト | 231 | 231 |
| レビュー系列 | 52 | 52 |
| 追加監査 | 8 | 8 |
| P2 系列 | 52(P2-a の 39 から追加:無効化の復元 1、登録の辺 3、防護例 7、固定 2) | 52 |
| 拡張の登録 | 10 件登録・拒否 0・防護例のない義務 3 | 同左 |
| 共通の評価(CLI) | P1 20 条件・80 JSON・417 必須パス、P0 221 必須パス、束 51a44343… |
同左 |
| 保存の監査 | 拡張 600/単項 256/存在規則 32/範囲 1 | (3.14 のみ) |
| 挙動の一致(分離の前後) | 22,052 件が一致 | 22,052 件が一致 |
extensions.py の変異(42) |
42/42 検出(P2-a の 37 に、登録の辺・推移的な依存の 5 を追加) | - |
| 防護例の変異(27+等価 1+対照 1) | 27/27 検出 | - |
extensions.py の変異検査では、登録の辺の規則を入れたとき、依存の推移閉包を壊す変異(X41)が最初は見逃された。人工の木のテストに「ホストが import するモジュールの拡張が、別の拡張を経由してホストに依存する」場合が無かったため。テストを足して検出した。
5. 未決と次
- 3 つの族のヘッダー検査の共通化は、挙動(エラーの path・code)を変えるので行っていない。必要なら契約の変更として扱う。
- 防護例のない義務 3 件(
R1-3、R3-3、COMP-3)は、拒否する入力を構成できないと判断したもの。独立の確認で覆り得る。 - 防護例の変異検査は、作成者が選んだ 27 件。変異演算子の網羅的な集合による検査は未実施。
- 独立確認は未実施(宣言・防護例・期待結果・欠落の理由は、すべて作成者の主張)。
- 次は P2-b(パイロット:概念の分割)。実装に入る前に、利用者の確認を取る。