# P28-02-EQ 仕様(spec_ref の候補。製品の変更なし。原本の加算は0) - 状態: 候補です。Root が原本の条件と照合して、`docs/plans/original-completion-2026-10-02/execution/p28-02/eq/SPEC.md` として main に固定し、その実 SHA を base にしてから、通常の新規登録をします。 - 前提: - P28-02-DIAG は正式に完了しています(Q `fdb309f3bc81529135915429d2432a6644696311`、`execution/p28-02/DIAG_FORMAL_COMPLETION_FDB309F.json` `7d83ec0e…`)。 - OPT の契約の候補は `OPT_CONTRACT_T1.md` r02 です。この SPEC の §3〜§5 が、その r02 の E1 の範囲・改善の判定・順序を確定し、置き換えます。 - 原本の条件: P28-02 の work と acceptance、横断条件6件は `execution/p28-02/SPEC.md` §1 のとおりで、変更しません。EQ は、OPT の前に同等性を固定するための補助の task で、原本の分母を増やしません(23/88、4/30)。 ## 1. 目的と範囲 - 原 work の「失敗時拒否を含む同等性を先に固定し」を、製品を変える前に、実行できる harness として凍結します。 - allowed_paths は `docs/plans/original-completion-2026-10-02/execution/p28-02/eq/**` だけです。製品の source map の1407件は変更しません。 - import の閉包: - RESTART の CLARIFICATION_02 D1 の6ファイル - DIAG の5ファイル(`execution/p28-02/diag/*`、読取専用) - 自分の allowed_paths の中のファイル ## 2. 成果物 | ファイル | 役割 | |---|---| | `eq/run_eq.py` | Root が実行する native の driver です。`--check \| --run --readonly <元の p28-01-readonly> --fixture --engine --output <新しい dir>`。owned phase・guard・private profile・cleanup は、collector を読取専用で import して使います | | `eq/eq_probe.gd` | E1〜E5 の probe です。起動は DIAG の6段階(OPUS_STARTUP_LIFECYCLE_CLARIFICATION)に従います | | `eq/compare_versions.py` | 2つの版の記録を比べる比較器です(§3) | | `eq/check_eq.py` | report の checker です | | `eq/verify_eq.py` | `--project-root .` で、`$BUILD_OUTPUT_DIR/p28-02-eq-evidence` にある実際の bundle を結合します。bundle が無ければ失敗します | | `eq/test_eq.py` | engine を使わない unittest です | ## 3. 比較の層(OPT r02 の E1 を確定する) ### 3.1 同じ PID の中(観測の副作用が無いこと) - DIAG と同じ、**完全な** S/K/C 層を、各観測の前後で比べます。K 層には、性能の telemetry(`performance_budget` の `_samples`・`_totals`・`_sample_counts`)を含みます。C 層には、instance ID を含みます。 - 前後で、bytes の完全一致を要求します。 ### 3.2 版と版の間(全状態と全イベントの意味の比較) - **S 層**: `create_snapshot()` の全体を比べます。`var_to_bytes` の完全一致を第一とします。不一致の場合に限り、RESTART の修正8の完全な typed の正規表現で比べ、dict の列挙の順序だけの差の path を全て記録します。それ以外は不一致です。`latest_snapshot` も同じ方法で比べます。`latest_digest` は、文字列の完全一致で比べます。 - **K 層**(版の間で比べる欄を、次のものに限ります): - absolute_minute、speed、paused、snapshot_version - `_accumulator`(f64 の hex。手動の stepping では決定的) - `_skip_next_process`、`_pause_request_serial`、`_abort_current_tick_requested`、`_last_tick_advanced` - `_phase_handlers` の、phase ごとの handler ID の集合 - `performance_budget._sample_counts` の metric ごとの**件数**(呼出しの回数で、決定的) - **K 層で、版の間では比べないもの**(理由: 実時間に依存し、同じ版の別の PID でも一致しないため): `_samples` と `_totals` の時間の値。各 PID の実際の値は、全て raw に保持します。 - **C 層**(版の間で比べる欄): - `_mvp_manifest_cache_generation` - 「`_mvp_manifest_cache_catalog` が `runtime.mvp_content_catalog` と同じ object か」の真偽 - `_mvp_manifest_cache` の `var_to_bytes` - **C 層で、版の間では比べないもの**(理由: プロセスごとに変わる値のため): instance ID の数値そのもの。実際の値は raw に保持します。 - **E2 の全イベント**: 開始の時点で、両方の版の domain_event の counter と published_ids の数が一致していることを要求します。そのうえで、event ID を**そのまま**対応させます(付け替えの対応表は作りません)。全ての欄の完全一致を要求します。開始の時点の counter が違えば、比較は不成立とし、失敗にします。 - 比べない欄を黙って落とすことはしません。比べない欄と、その理由と、各版の実際の値を、比較の結果に全て記録します。SimulationDigest だけでの判定は不可です。 ### 3.3 観測と、意図的な scenario 操作の境界 - (a) **観測の区間**: 記録・比較・snapshot の取得などの観測そのものは、§3.1 のとおり、前後で完全な S/K/C が不変であることを要求します。 - (b) **scenario の区間**: E3/E4/E5 のために列挙した操作だけは、観測の区間とは別の、専用の区間で行ってよいものとします。それぞれの区間には開始と終了の記録を付け、前後の S/K/C の変化、拒否の code、所有権の結果を、実際の値として記録します。列挙した操作は次のとおりです。 - E3: 不正な入力を与えた `restore_snapshot`・`SimulationSnapshot.validate`・`load_from_path` - E4: 状態を変えるコマンド(建設のコマンドを1件、通常の command queue を通す)、tick の進行(`advance_minutes(1)`)、`restore_snapshot`、`load_from_path`、状態を変えないコマンド - E5: 返された値(`create_snapshot()` の戻り値、`latest_snapshot`、`snapshot_published` / `state_updated` の引数)の、入れ子の dict と配列の変更 - (c) **製品の既存の失敗との区別**: E5 の操作によって live の状態が変わった場合(たとえば、`latest_snapshot` と signal の引数の aliasing。OPT r02 §3 で予想したもの)は、製品の既存の失敗として、変化した path と値を記録して保持します。これは harness による無断の変更ではありません。どの scenario の、どの操作の結果かを、記録の中で結びつけます。その scenario の後の検査は、新しい baseline から始めます。 - (d) **禁止**: 上に列挙した scenario の操作以外の、状態の変更と、hook や関数の差し替え。起動の準備(OPUS_STARTUP_LIFECYCLE_CLARIFICATION の段階①②)と手動の stepping は、従来どおりです。 - 原本の全状態・全イベント・受入の条件は減らしません。K/C の限定の例外(§3.2)も、そのままです。 ## 4. 改善の判定(OPT の受入で使う。新しい数値の閾値は作らない) - 指標(PID または run ごとに1つの値): - (a) I11:PID ごとの、測定30 tick の I11 の平均(inclusive の wall) - (b-1) P1:run ごとの、kernel の `simulation_x1` の budget_verdict の値(区間の標本の平均。collector の H1 の定義) - (b-2) P2:run ごとの、kernel の `simulation_x3` の budget_verdict の値(区間の標本の P95) - 前後それぞれ3つの値で、指標ごとに分類します: - **改善**: 後の版の3つの最大が、前の版の3つの最小より小さい。 - **悪化**: 後の版の3つの最小が、前の版の3つの最大より大きい。 - **差は確認できない**: それ以外(範囲が重なる)。 - P95・最大・stall の最大、I0〜I10 の各区間の値は、記述として全て報告します。分類には使いません。 - OPT の受入での扱い: - 「改善あり」と書いてよいのは、(a) が「改善」で、かつ (a)・(b-1)・(b-2) のどれも「悪化」でない場合だけです。 - (a) が「改善」にならなければ、「改善を確認できない」として、OPT の変更を採用しません(原 work の「改善する」を満たさないため)。 - (b) が「改善」でなくても、悪化でなければ構いません。そのまま記録します。 ## 5. 順序と、harness の正しさと製品の受入の区別 1. EQ の worker が S_eq を提出します(engine を使わない局所の unit の全件 PASS つき)。 2. Root が、凍結した S_eq で native を実行します。phase は7つです: - import - parse-base と parse-wrapper(`--check-only`) - R-A、R-B(R の同じ版の2 PID) - O-A、O-B(O の同じ版の2 PID) 3. Root が raw を結合して bundle を作り、`$BUILD_OUTPUT_DIR/p28-02-eq-evidence` に置きます。 4. 正式の validation を行います。`p28-02-eq-unit` と `p28-02-eq-native-verify` の両方が必須です。 5. 独立した新しい Opus がレビューします(非同期の配送)。 6. prepare → Q_eq の資格の確認 → Root の委任による承認 → integration → final。 - **before の記録**: OPT の比較で使う「変更の前の版」の EQ の記録は、手順2の S_eq の native です(製品の1407件は、変更の前と同じです)。Q_eq の資格の確認で、製品の source map・engine・入力・設定・EQ のファイルが一致した場合に限り、そのまま再利用します。一致しなければ、Q_eq でもう一度実行します。 - **harness の正しさ**(EQ の受入の条件): 同じ版の2 PID の間で、§3.2 の比較の差が0であること(決定性)。E3/E4/E5 の検査が実際に動いて、結果を記録していること。拒否試験がそろっていること。 - **製品の結果**(EQ の受入の条件では**ない**): 前の版での E3/E5 の結果は、事実として記録するだけです。失敗したもの(たとえば OPT r02 §3 で予想した E5 の外から内への aliasing)は、**既存の不具合**として一覧にし、PASS で補いません。それを直すのは OPT の範囲です。 ## 6. 全ての失敗の保持 - native・validation・review の失敗は、raw と SHA とともに保全し、成功に書き換えません。修正は、通常の repair で、同じ W で行います。