# MISAKA PALW Receipt 実装仕様 — Qwen3.6-35B-A3B (hybrid MoE) 本書は、対象モデルを dense Qwen3-8B から hybrid **Qwen3.6-35B-A3B** (`huihui_ai/Qwen3.6-abliterated:35b-Claude-4.7`、abliterated) へ移行した後の `ComputeReceiptV1` 発行・検証パイプラインの実装仕様である。記述は現行 Rust/C++ ソースを正本とし、実装と本書が一致しない場合は Receipt を発行しない ([`protocol-v1.md`](protocol-v1.md) と同じ原則)。本書に無い production 承認・決済連携・ attestation を Receipt から推論してはならない。 対象モデルは linear-attention(state-space / gated-delta-net)層と mixture-of-experts 層を組み合わせた hybrid 構成であり、dense Qwen3-8B 用の厳格な canonical operation mapping では表現できない演算を含む。そのため本移行では **(1)** pinned llama.cpp への qwen35moe loader/graph 互換修正、**(2)** dense vocabulary に対応の無い演算を output-element 数で committ する generic operation と CU ルールセット v2、**(3)** profile 駆動の hybrid adapter/manifest、を追加した。移行完了後、dense Qwen3-8B 経路(profile / manifest builder / constants / tests)は撤去され、Qwen3.6-35B-A3B が唯一の対象モデルとなった。 行番号 anchor は執筆時点のものであり、正本はソースである。 > **compute-gate track 更新(2026-07-19、実機検証済み)。** 上記 (2) の「generic operation + CU > ルールセット v2」は**後続の compute-gate track で置換された**。現行 receipt は **(M1)** CU を > semantic ルールセット v3 で canonical commit(`canonical_compute_units = 41692`)、**(M3)** generic > 演算を廃止し全 MoE/SSM op を忠実な canonical 演算(`ExpertGemm`/`ExpertRoute`/`SsmConv`/ > `GatedDeltaNet`/`L2Norm`/`Reduction`/`Elementwise`)へ写像、**(M2)** canonical semantic schedule を > 再生成・commit、**(M4/#6)** observer が実 MoE routing(`ffn_moe_topk`)を捕捉し `expert_route_root` > を封緘 bundle へ commit(observer schema v2)、**(M5/#5)** ggml-metal を計装して各 GEMM を実 Metal > kernel dispatch + launch geometry へ束縛し `trace_evidence=metal_kernel` を commit(graph-fallback > から昇格、実機で 200/200・411/411 検証)。§0–§2 の一部記述(CU v2 / generic / SCHEMA_VERSION 1 / > MUL_MAT_ID=metadata / graph_fallback)は旧版で、現行の正確な値は §10・§11 と > [`receipt-review-remediation.md`](receipt-review-remediation.md) を参照。**6 つの compute gate は > 全て実機検証済み。** Metal の kernel-level trace は launch-geometry 束縛であり CUDA V3 相当の > intra-kernel accumulator proof ではない(honest labeling、mint は依然不適格)。 --- ## 0. 全体アーキテクチャとデータフロー 三層構成: 1. `vendor/llama.cpp/`(commit 固定 + PALW patch): 推論ランタイムと read-only graph observer `llama-palw-observer`。 2. `runtime-palw/`(Rust): adapter、canonical CU、schedule、commitment、manifest、receipt builder/verifier、verification bundle、schema-v4 SQLite。 3. `config/` / `patches/`: 固定 artifact pin、model profile pin、llama.cpp patch。 Receipt 発行(`palw-metal-receipt`)のデータフロー: ``` prompt(stdin) │ ├─ build_qwen36_35b_metal_manifest ─────────► RuntimeManifest(MoE topology, CU v3 semantic) │ ├─ llama-tokenize ──────────────────────────► prompt_token_ids │ ├─ llama-palw-observer --observer sketch ───► JSONL(header/event*/result) │ │(read-only graph callback、sketch) │ ▼ │ qwen_adapter(HybridQwen36A3B) │ │ header 検証 + op→ComputeOperation 写像 + GEMM sketch │ ▼ │ RuntimeObserver → ExecutionEvidence(schedule + GEMM trace + CU total, ruleset=v3 semantic) │ ├─ build_signed_receipt ────────────────────► ComputeReceiptV1(署名) ├─ verify_stateless(cu_rules=v3) ───────────► 自己検証 ├─ seal_verification_bundle(XChaCha20) ─────► .palw.bundle ├─ StateStore(schema v4).accept ────────────► palw-state.sqlite3 └─ 書き出し: .palw / .json / .palw.bundle / .complete ``` 別 process 検証(`palw-verify-bundle`)は artifact set・完了 marker を検査し、bundle を復号・ 再検証(`verify_bundle_and_restore`)して `status=local_restored` / `trust_scope=embedded_local_snapshot` を出力する。 --- ## 1. 対象モデルと固定 artifact 正本: [`config/runtime-pins.sh`](../config/runtime-pins.sh)(公開識別子のみ、末尾で全て `readonly`)。 ### 1.1 推論 GGUF(Ollama registry blob) | 変数 | 値 | |---|---| | `PALW_GGUF_REGISTRY` | `registry.ollama.ai` | | `PALW_GGUF_REPO_ID` | `huihui_ai/Qwen3.6-abliterated` | | `PALW_GGUF_OLLAMA_TAG` | `35b-Claude-4.7` | | `PALW_GGUF_REVISION` = `PALW_GGUF_SHA256` | `1dc494614bee8a3bc00e79fe5a49da0fc1c36b3b118c4156e223e98e5a0a671b` | | `PALW_GGUF_FILENAME` | `Qwen3.6-abliterated-35b-Claude-4.7-Q4_K_M.gguf` | | `PALW_GGUF_SIZE` | `23938321728`(bytes) | Ollama registry の blob digest は GGUF 本体の SHA-256 に等しいため `revision == sha256`。 content 検証だけで pin 全体の照合になる。 ### 1.2 base metadata(Hugging Face、7 files) `PALW_BASE_REPO_ID = huihui-ai/Huihui-Qwen3.6-35B-A3B-Claude-4.7-Opus-abliterated`、 `PALW_BASE_REVISION = ac18882735d037f6074a7630eb68d85db8234c25`。 files: `README.md`、`chat_template.jinja`、`config.json`、`model.safetensors.index.json`、 `processor_config.json`、`tokenizer.json`、`tokenizer_config.json`。 sha256 は [`config/qwen3.6-35b-a3b-claude-4.7-base-metadata.sha256`](../config/qwen3.6-35b-a3b-claude-4.7-base-metadata.sha256) および profile pin の `base_artifact.files[]` と一致する。 ### 1.3 llama.cpp `PALW_LLAMA_COMMIT = 12127defda4f41b7679cb2477a4b0d65ee6a0c8f`(build 10015)、 patch = [`patches/llama.cpp-palw-observer.patch`](../patches/llama.cpp-palw-observer.patch)、 `PALW_LLAMA_PATCH_SHA256 = d155a88b7c11ee74f48011760cb1a37773a694c8cab28258ee108c85e2f9e02c`。 Metal build は `-DGGML_METAL=ON -DGGML_CUDA=OFF`、`arm64`、target `llama-cli/llama-server/llama-tokenize/llama-palw-observer`。 ### 1.4 Ollama download 経路(`install.sh :: download_gguf_if_needed`) 1. `gguf_content_matches`(存在 + size + sha256)が成立すれば再利用。 2. mismatch する既存 file は上書きせず die。 3. URL = `https://$PALW_GGUF_REGISTRY/v2/$PALW_GGUF_REPO_ID/blobs/sha256:$PALW_GGUF_REVISION` (OCI/Docker registry の blob-by-digest endpoint)。 4. `curl -fSL -C -`(再開可能)で `.partial` に取得。 5. size と sha256 を照合してから `mv` で atomic に確定。 6. base metadata は従来通り HF(`hf_snapshot_download.py`、token を strip し Xet を無効化)。 --- ## 2. 決定論的実行と native observer 正本: `vendor/llama.cpp/tools/palw-observer/palw-observer.cpp`(以下 OBS)。単一リクエストの 非対話 runner で、public な `llama`/`ggml` API のみ使用。versioned JSONL を stdout に、 llama/ggml のログを stderr に書く。 ### 2.1 強制される決定論的実行ポリシー `n_ctx = PALW_CONTEXT_TOKENS = 4096`、`n_batch = n_ubatch = 1`、`n_seq_max = 1`、 `n_threads = n_threads_batch = 1`、greedy sampler、`flash_attn = DISABLED`、 `type_k = type_v = F16`、`split_mode = NONE`、`use_mmap = true`。context 初期化後に 実 context を読み直し、この不変条件を満たさなければ exit 3。prompt は `--prompt-stdin` 専用 pipe で渡し(argv に置かない)、prefill/decode とも 1 token ずつ `llama_batch_get_one` で評価する。`prompt_tokens + n_predict > 4096` は context-shift せず失敗。 ### 2.2 model profile gate(`validate_qwen36_35b_a3b_profile`) model load 直後に、`general.architecture == "qwen35moe"`、`n_layer == 40`、`n_embd == 2048`、 `n_vocab == 248320`、decoder-only(encoder 無し)のみ受理。不一致は header/result を一切 emit せず exit 3。 ### 2.3 JSONL プロトコル `SCHEMA_NAME = "misaka.palw.runtime_observer"`、`SCHEMA_VERSION = 2`(route record 追加)。1 実行は **header 1** → **event 0..N** → **result 1**。各行は個別に flush。 - **header**: `observer`(off/graph/sketch)、`trace_variant`、`cuda_kernel_trace:false`、 `model{path,description,tensor_size_bytes,parameter_count,file_type,n_ctx_train,n_embd, n_layer,n_head,n_head_kv,n_vocab,rope_type,rope_freq_scale_train,metadata[]}`、 `runtime{ggml_version,ggml_commit,system_info,supports_gpu_offload,requested_gpu_layers, devices[]}`、`execution_policy{...上記決定論ポリシー...}`、 `observation_policy{read_only:true, graph_metadata_stage:"ask", sketch_bits:256, sketch_probe:"gemm_output_contiguous_prefix_64", raw_activation_values_published:false, tile_variant:"graph_fallback_logical_prefix_v1", kernel_trace_claim:"none"}`。 - **event**: `event_seq`(単調増加、最終値 = result の `event_count`)、`phase` (prefill/decode)、`phase_step`、`stage`(ask_metadata / post_compute_sketch)、`layer` (名前から導出、無ければ null)、`categories[]`、`tensor{name,op,type,shape[4],n_bytes}`、 `sources[]`。MUL_MAT には `gemm{variant:"ggml_graph_op_v1",m,n,k,batch_shape}`。sketch 時のみ `sketch{}`/`tile{}`/`kernel_trace{}`。 - **result**: `status`、`stop_reason`(n_predict/eog/…)、`prompt_token_ids[]`、 `generated_token_ids[]`、`output_bytes[]`(`--emit-output-bytes` 時のみ非空)、 `output_n_bytes`、`event_count`。 ### 2.4 observer モードと sketch scheme - `off`: callback 無し、event 0。 - `graph`: 各非空 valid node に対し `ask_metadata` を 1 件 emit。tensor data は一切読まない。 - `sketch`: 非 MUL_MAT node は `ask_metadata`。`MUL_MAT` node のみ post-compute で `ggml_backend_tensor_get` により先頭最大 64 要素を読み、**sign/bucket sketch** を採取して `post_compute_sketch` を emit。 sketch(`sign_bucket_256_v1`): 各要素を 1 nibble(bit3 = 符号、bit0..2 = 3-bit 絶対値 bucket)にし、64 nibble = **256 bit**。bucket 境界は `0, 2⁻⁸, 2⁻⁴, 2⁻², 1, 4, 16, ∞`。生の 活性値は決して serialize しない。`kernel_trace{available:false, backend:"none", claim:"not_a_cuda_kernel_trace"}` を常に付し、graph 観測を CUDA kernel 証拠へ昇格しない。 ### 2.5 observer 側の hybrid 対応(3 点) 1. **`is_gemm` を `GGML_OP_MUL_MAT` のみに限定**。`GGML_OP_MUL_MAT_ID`(indirect expert GEMM)は sketch せず通常の metadata node として emit する。 2. **0-element(recurrent state cache)tensor を skip**。gated-delta-net / SSM 層は初期状態が 空の state cache を gather し、合法な 0-element node を生む。これは compute を伴わないため error にせず skip。 3. **`validate_tensor` が空 tensor を許容**(name/type/op の範囲検査後に短絡)。 ### 2.6 qwen35moe.cpp loader/graph 修正(5 点) pinned commit `12127def` の `src/models/qwen35moe.cpp` は本モデルを load/run できないため、 PALW comment 付きで以下を修正(observer patch に同梱): 1. **3-section mrope zero-pad**: `rope.dimension_sections` を可変配列として読み、size 3 or 4 を 許容して zero-fill+copy。新しい HF→GGUF 変換の `[t,h,w]`(末尾 0 省略)を受理。 2. **`ssm_dt` bias の suffix 両対応**: `blk.N.ssm_dt.bias` を `TENSOR_NOT_REQUIRED` で試し、 無ければ suffix 無し `blk.N.ssm_dt` を required で load。 3. **KV 射影幅に `n_embd_k_gqa_max()`**: `LLAMA_LOAD_LOCALS` は layer 0 から `n_embd_k/v_gqa` を導出するが、layer 0 は linear-attention 層で `n_head_kv==0`。full-attention trunk 層と MTP 層の tensor 生成で per-layer 最大値を使う。 4. **sibling tensor(`v.*` / `mtp.*`)の計上**: Ollama 同梱 GGUF は vision tower と(MTP 未 有効時)MTP sub-model を同梱する。text runtime はこれらを構築しないため、該当 prefix の tensor 分だけ `n_created`/`size_data` を調整し `done_getting_tensors` を通す。存在する tensor のみ 1 回ずつ計上するため、text tensor の欠落は依然として検出される。 5. **`build_layer_attn` の per-layer `n_head(il)`/`n_head_kv(il)`**: graph base は layer 0 から head 数を初期化するため、full-attention の Q/K/V reshape と RoPE を per-layer 値で shadow。 --- ## 3. Rust adapter — hybrid 経路 正本: `runtime-palw/src/qwen_adapter.rs`。`AdapterProfile { HybridQwen36A3B }`(唯一の profile) を `NativeParseConfig`/`NativeRunConfig` の `profile` field で選択し、 `parse_native_jsonl_with_stderr` が header 検証 / event 変換 / vocab 上限を hybrid 経路で処理する。 旧 dense 経路は撤去済み。 ### 3.1 hybrid 定数と識別子 `QWEN36_35B_LAYERS=40`、`QWEN36_35B_VOCAB=248320`、`QWEN36_35B_EMBED=2048`、 `QWEN36_35B_CONTEXT=262144`、`QWEN36_35B_PARAMETERS=35_951_822_704`、 `QWEN36_ARCHITECTURE="qwen35moe"`、共有の `Q4_K_M_FILE_TYPE=15`、`QWEN_RUNTIME_CONTEXT=4096`。 `qwen36_adapter_mapping_id_v1()` は domain `"qwen36-native-observer-mapping/v1"` の `domain_hash`。payload は各 op の写像を列挙し `cu-ruleset=v2;graph-fallback-only` を含む。dense の `"qwen-native-observer-mapping/v1"`(`soft_max=attention`)とは別 hash。 ### 3.2 header 検証(`validate_header_hybrid`) deterministic execution policy と observation policy は **dense と同一**の厳格さで検証する。 model-shape だけを MoE 向けに一般化する: - `parameter_count == 35_951_822_704`、`file_type == 15`、`n_ctx_train == 262144`、 `n_embd == 2048`、`n_layer == 40`、`n_vocab == 248320`、`rope_freq_scale_train == 1.0`。 - **dense と違い `n_head`/`n_head_kv`/`rope_type` は pin しない**(MoE は per-layer で可変、 layer-0 KV-head を制約しない)。 - 必須 metadata: `general.architecture=qwen35moe`、`general.file_type=15`、 `general.quantization_version=2`、`qwen35moe.context_length=262144`、 `qwen35moe.embedding_length=2048`、`qwen35moe.block_count=40`、 `qwen35moe.expert_count=256`、`qwen35moe.expert_used_count=8`。dense と逆に **expert metadata を要求**する(dense は expert metadata を拒否)。 - graph/sketch 時は `supports_gpu_offload && requested_gpu_layers==999 && GPU device 存在`。 ### 3.3 op → canonical operation 写像(`convert_event_hybrid`) 各 ggml op を canonical `ComputeOperation` へ写像する。dense と異なり、正確な canonical 対応がある演算はそれへ、対応の無い演算は `Generic{op_class, elements}` へ写す。fail-closed。 | ggml op | canonical operation | |---|---| | `MUL_MAT` | `Gemm`(`convert_gemm_hybrid`、sketch 採取) | | `RMS_NORM` | `RmsNorm { elements=shape[0], batch=Πshape[1..] }` | | `ROPE` | `Rotary { head_dim=shape[0], heads=shape[1], tokens=Πshape[2..] }` | | `SOFT_MAX` | `Softmax { elements=Πshape }`(dense は Attention 再構成) | | `GLU` | `Silu{elements}` + `ElementwiseMultiply{elements}` | | `MUL` | `ElementwiseMultiply { elements }` | | `ADD` | `ElementwiseAdd { elements }` | | `GET_ROWS` | `EmbeddingLookup { elements }` | | `SET_ROWS` | `KvCacheWrite { bytes = tensor.n_bytes }` | | `CONT` \| `CPY` | `TensorCopy { bytes = tensor.n_bytes }` | | `VIEW` \| `RESHAPE` \| `PERMUTE` \| `TRANSPOSE` | layout-only(schedule に載せない) | | その他 | `Generic { op_class=hybrid_generic_op_class(op), elements=Πshape }`。未列挙は拒否 | `hybrid_generic_op_class`(不変な op→u32 表): `MUL_MAT_ID=1`、`UNARY=2`、`SCALE=3`、 `L2_NORM=4`、`SUM_ROWS=5`、`DIV=6`、`CLAMP=7`、`ARGSORT=8`、`SSM_CONV=9`、 `GATED_DELTA_NET=10`、`CONCAT=11`。これ以外は `None` → `InvalidEvent`(fail-closed)。 ### 3.4 GEMM 変換(`convert_gemm_hybrid`) `gemm{}` metadata の相互整合(`m==left.shape[1]`、`n==right.shape[1]`、`k==left.shape[0]`、 `left.shape[0]==right.shape[0]`、output shape 一致)を検証する。dense と異なり **固定 geometry や dtype allowlist は課さない**。quantization は `left.kind` から `q4_K→Q4KM`、`q6_K→Q6K`、 その他→`None`。sketch 時は `validate_graph_fallback_sketch`(sketch/tile/kernel_trace の値を 厳格検証)後、64 hex を 4 lane の big-endian `i64` へ復元し `on_gemm_group` で trace に記録。 kernel_id domain は `"qwen36-graph-fallback-probe/v1"`。 ### 3.5 GPU offload marker とプライバシ `scan_stderr_markers` は `"offloaded N/N layers to gpu"` を **N が正で左右一致なら任意の N** で 検出する(dense 37/37、hybrid 41/41 を model 非依存で受理)。不一致は `IncompleteGpuOffload`。prompt は `OsStr` の stdin のみ、error payload は `&'static str`/数値 のみ、`QwenObserverOutput` は `Debug` 未実装、`zeroize_private_material` が prompt/生成 token/QA bytes を zeroize。 --- ## 4. 正準演算語彙と CU ルールセット 正本: `runtime-palw/src/compute_units.rs`。 ### 4.1 `ComputeOperation`(canonical serialize discriminant) `0 Gemm(GemmShape)`、`1 Attention`、`2 LayerNorm`、`3 RmsNorm`、`4 Rotary`、`5 ExpertRoute`、 `6 KvCacheRead`、`7 KvCacheWrite`、`8 Silu`、`9 ElementwiseMultiply`、`10 Softmax`、 `11 ElementwiseAdd`、`12 TensorCopy`、`13 EmbeddingLookup`、**`14 Generic{op_class:u32, elements:u64}`**(本移行で追加)。0..5 は `u32` 次元、6..13 は単一 `u64`。追加は additive で 既存 variant の byte は不変。 `Generic` は「dense に cost 規則の無い演算(gated-delta-net、SSM conv、MoE routing 等)を 意味を歪めず、output 要素数で忠実に committ する」ための variant。`op_class` は演算種別の 安定 ID、cost は output 要素数のみに依存する。**v2 のみが pricing し、v1 は拒否する**ため 既存 dense receipt と ruleset identity は不変。 ### 4.2 CU ルールセット v1/v2 `CU_RULESET_VERSION_V1=1`、`CU_RULESET_VERSION_V2=2`。`ruleset_id()` は `domain_hash("cu-ruleset/vX", X, [payload])`。payload の唯一の差分は v2 が `ceil;min1` の前に **`generic=2/4096;`** を挿入する点(domain も `/v2`)。よって `v1().ruleset_id() != v2().ruleset_id()`。`matching_ruleset_id(&Hash32)` は `[v1(),v2()]` から 一致する ruleset を返す(本移行で追加、bundle 経路が使用)。 cost 式(`cost()`、`scaled_cost = max(1, ceil(work·units/reference))`、全て checked): | op | work | reference | units | |---|---|---|---| | `Gemm` | m·n·k·batch | 4096³ | 120 | | `Attention` | q·kv·heads·head_dim·batch | 4096·4096·32·128 | 80 | | `LayerNorm`/`RmsNorm` | elements·batch | 4096 | 8 | | `Rotary` | tokens·heads·head_dim | 4096 | 4 | | `ExpertRoute` | tokens·experts·top_k | 4096 | 16 | | `KvCacheRead`/`Write`/`TensorCopy` | bytes | 1 MiB | 1 | | `Silu`/`ElementwiseMultiply`/`ElementwiseAdd` | elements | 4096 | 2 | | `Softmax` | elements | 4096 | 4 | | `EmbeddingLookup` | elements | 4096 | 1 | | **`Generic`** | elements | 4096 | 2 (**v2 限定**、v1 は `UnsupportedOperation`) | `ComputeUnitCounter.record` は cost を checked-add し、overflow 時は state を変えず `CostError::Overflow`(fail-closed)。 --- ## 5. Model profile pin と RuntimeManifest 構築 正本: `runtime-palw/src/qwen_profile.rs`、`runtime-palw/src/manifest.rs`、 [`config/qwen3.6-35b-a3b-claude-4.7-profile.json`](../config/qwen3.6-35b-a3b-claude-4.7-profile.json)。 ### 5.1 profile pin(schema v2) `schema = "misaka.palw.model-profile-pin.v2"`、 `profile_name = "Qwen3.6-35B-A3B-Claude-4.7-Opus-abliterated-Q4_K_M-Metal-arm64"`。 `model{}` は MoE の全 shape(architecture `Qwen3_5MoeForConditionalGeneration`、model_type `qwen3_5_moe`、gguf_architecture `qwen35moe`、hidden 2048、heads 16、kv 2、layers 40、 head_dim 256、intermediate 512、expert_count 256、experts_per_token 8、rope_theta 10000000、 vocab 248320、max_pos 262144、bfloat16、Q4_K_M、ほか linear_* 等)を持つ。`base_artifact` は 7 files(各 sha256)、`inference_artifact` は Ollama repo/tag/**64-hex blob revision**/bytes/ sha256、`runtime` は commit と cmake_cache。deserialize struct は serde `deny_unknown_fields` (`HybridProfilePin`/`HybridModelPin`/`HybridInferenceArtifactPin`。dense との差は `tag` field)。 ### 5.2 `validate_hybrid_pin` schema/profile_name 一致、model 全 shape 一致、`download_scope=="metadata-only"`、 `supported_profile=="metal-arm64"`、`targets` に `llama-palw-observer`、`commit.len()==40`、 `base.revision.len()==40`、**`inference.revision.len()==64`**(Ollama blob digest)、 `inference.bytes!=0`。metadata files は 7 件で各 sha256 を streaming 照合 (`verify_base_metadata_hybrid`)。 ### 5.3 `build_qwen36_35b_metal_manifest` host(arm64、8 field 非空)と environment(`LANG`/`LC_ALL` のみ)を検証し、GGUF の size/sha256、 tokenizer(`tokenizer.json`+`tokenizer_config.json` の 2 件)を artifact 化する。以下の digest を `domain_hash` で計算し RuntimeManifest に committ する(dense と domain string が異なる主要点): - `chat_template_digest`(`chat-template/v1`): **`chat_template.jinja` file bytes** (dense は tokenizer_config 内の文字列)。 - `special_tokens_digest`(`special-token-semantics/v1`): tokenizer_config + config.json (generation は `Value::Null`)。 - `kernel_graph_id`(`qwen36-35b-llama-graph/v1`): commit + `qwen36_adapter_mapping_id_v1()` + `ggml_sched_ask_metadata_v1` + prefill/decode 直列。 - `kernel_algorithm_digest`(`kernel-algorithm-policy/v1`): 末尾に `;moe-hybrid;cu-ruleset=v2`。 - ほか `loaded_libraries_digest`、`build_flags_digest`、`dirty_patch_digest`、 `environment_allowlist_digest`、`runtime_device_digest`、`runtime_program_digest`、 `build_provenance_digest`。 `ModelProfile` は **topology = MixtureOfExperts**、`kv_heads=2`、**`head_dim=256`(hidden/heads = 2048/16 = 128 とは decouple)**、`rope="Qwen3.5-iMRoPE"`、`native_context_tokens=262144`、 `vocabulary=248320`。`RuntimeClass` は Q4_K_M、flash-attn off、`context_size=4096`、batch=1。 `RuntimeManifest.cu_ruleset_id = ComputeUnitRules::v2().ruleset_id()`。 ### 5.4 manifest validator の緩和(hybrid を受理する 2 点) - `ModelProfile::validate` は旧来の `head_dim == hidden/heads` 検査を**削除**した。head_dim を 独立 field として committ し cross-check しないため、`head_dim=256` と `2048/16=128` が 共存できる。`hidden % heads == 0`(2048%16)、`heads % kv == 0`(16%2)は維持。 - `RuntimeManifest::validate` は `cu_ruleset_id` が **v1 または v2** の ruleset_id なら受理する。 --- ## 6. Receipt body・commitment・署名 正本: `runtime-palw/src/receipt.rs`、`runtime-palw/src/builder.rs`、`runtime-palw/src/observer.rs`。 ### 6.1 `ComputeReceiptV1`(署名される canonical wire 順) 30 field を固定順で encode し、その byte 列が全 digest(`signing_digest`/`body_id`/ `receipt_id`)の入力になる。主要 field: `receipt_version(=1)`、`network_id`、 `request_commitment`、`scheduler_job_id`、`signed_assignment_id`、`replica_slot`、 `model_profile_id`、`runtime_class_id`、`runtime_manifest_hash`、`shape_profile_id`、 **`cu_ruleset_id = evidence.cu_ruleset_id()`**、`trace_scheme_id`、`trace_evidence`、 `operation_schedule_commitment`、`schedule_event_count`、`canonical_compute_units`、 `prefill_tokens`、`decode_tokens`、`output_commitment`、`gemm_trace_root`、`trace_event_count`、 `owner_commitment`、`worker_credential_id`、`job_nullifier`、`execution_nullifier`、`job_class`、 `evidence_level`、`timestamp`、`issued_epoch`、`expires_epoch`。 `validate_structure` は署名前後で 17 digest の非零、`prefill_tokens!=0`、 `issued<=expires`、job_class ごとの replica_slot 範囲、evidence_level ごとの count 整合 (GemmTraced は schedule/trace/CU が全て非零かつ `trace_evidence!=Absent`)を検査する。 ### 6.2 `build_signed_receipt` `manifest.validate`、CUDA KernelSketch 拒否、scheduler 署名(request+assignment)検証、worker credential binding、request constraints と manifest ID の一致、evidence↔shape/job binding、 prompt opening と `prefill_tokens` 一致、output count 一致、を検査後に commitment (`commit_prompt_tokens`/`commit_output`/`commit_owner`/`derive_job_nullifier`/ `derive_execution_nullifier`)を計算し、body を Ed25519 署名(`signing_digest` は network-id salt 付き canonical bytes)。builder は `cu_ruleset_id` を **evidence の値そのまま** committ する (独自に ruleset を再導出しない)。 ### 6.3 evidence 生成(`RuntimeObserver` → `ExecutionEvidence`) observer は `job_nullifier`/`shape_profile_id`/`ComputeUnitRules`/`EvidenceLevel` に束縛される。 `schedule = OperationSchedule::new(..., rules.ruleset_id())`、trace は GemmTraced 時のみ生成。 状態機械は `AwaitingPrefill→Prefill→Decode{step}`、decode step は厳密連番、任意の event error で 恒久 poison。`finalize_with_generated_tokens` は `generated == decode_tokens+1`(先頭 token は prefill logits から sample)を要求。GemmTraced 不変条件として **schedule の GEMM 数 == trace group 数**。`ExecutionEvidence.cu_ruleset_id = counter.rules().ruleset_id()`(本移行では v2)がそのまま receipt へ流れる。 --- ## 7. Stateless 検証(`verify_stateless`) 正本: `runtime-palw/src/verifier.rs`。`VerificationContext { network_id, current_epoch, manifest, cu_rules, signer_registry, assignment_authorizations }` を取り、順に: 1. `validate_structure`、network/epoch。 2. assignment 解決と全 field binding、assignment policy(`required_evidence_level==evidence_level`)。 3. signer 解決、owner/worker commitment、**Ed25519 署名**(weak-key 拒否 + `verify_strict`)。 4. `manifest.validate`、CUDA KernelSketch 拒否。 5. `model_profile_id`/`runtime_class_id`/`runtime_manifest_hash`/`shape_profile_id` を manifest から独立再計算して照合。 6. **CU-ruleset triple-bind**: `body.cu_ruleset_id == context.cu_rules.ruleset_id()` **かつ** `== manifest.cu_ruleset_id`(不一致は `CuRulesetMismatch`)。本移行では三者とも v2。 7. `trace_scheme_id` bind、`job_nullifier`/`execution_nullifier` の再導出照合、`verify_shape`。 8. `verify_execution_evidence`: evidence metadata・schedule integrity/commitment 照合後、 **`context.cu_rules` で schedule を最初から再 pricing** し `canonical_compute_units` を 照合(claim を信用しない)。per-entry で layer 範囲、dense topology の expert-route 拒否、 decode phase 順序を検査。GemmTraced は `verify_trace`(trace binding、capability 一致、 GEMM 数 == trace group 数、per-event の schedule 対応)。 9. output nonce / prompt opening / output opening / output count。 hybrid では topology が MixtureOfExperts のため expert-route の dense 拒否は発火しない(なお 本 adapter は `MUL_MAT_ID` を `Generic` に写すので ExpertRoute op 自体を生成しない)。 --- ## 8. Verification bundle と ruleset 選択の 3 修正 正本: `runtime-palw/src/verification_bundle.rs`。 ### 8.1 `seal_verification_bundle` owner opening を照合し、**封印前に receipt を完全に再 stateless 検証**する(state-store は 変更しない)。公開部 `BundlePublicV1`(receipt_binding、receipt_id、verification_epoch、 scheduler-key record、`manifest.canonical_bytes()`、`PortableEvidenceV1`)は認証済み cleartext。 秘密部 `BundlePrivateV1`(署名済み request/assignment、signer record、output opening = output_nonce + prompt/output token IDs、owner salt)は **XChaCha20-Poly1305** で AEAD 暗号化 (AAD で receipt_binding/public_digest/nonce/public_bytes を束縛、AEAD 鍵は audit key の `domain_hash` 派生で raw key を直接使わない)。`bundle_id` は完全な暗号化 envelope 全体の `domain_hash`。 ### 8.2 CU ルールセット選択の 3 修正(v2 対応) | # | 位置 | 修正 | |---|---|---| | 1 | seal 経路 | `cu_rules = ComputeUnitRules::matching_ruleset_id(&context.manifest.cu_ruleset_id)`(旧: v1 固定) | | 2 | `verify_bundle_stateless_inner` | `matching_ruleset_id(&bundle.public.manifest.cu_ruleset_id)`(旧: v1 固定) | | 3 | `PortableEvidenceV1::encode_canonical` | ruleset **version** を evidence に合わせて書く(`matching_ruleset_id(evidence.cu_ruleset_id()).map_or(0, version)`。旧: v1 固定)。decode は `for_version` で読む | これらが無いと、v2 evidence が bundle 内で v1 として round-trip し `CuRulesetMismatch` になる。 併せて `manifest.rs` の validate が v1|v2 を受理し、`matching_ruleset_id` を追加した。producing CLI は `ComputeUnitRules::v2()` を bind する。 ### 8.3 portable evidence の round-trip encode は version・level・prefill/decode・**ruleset version**・job_nullifier・shape_profile_id・ schedule entries・trace groups・claimed CU・claimed ruleset を書く。decode は `RuntimeObserver` で schedule/trace/CU を **再導出**(serialize 済み total を信用しない)し、再構成 evidence の `canonical_compute_units`/`cu_ruleset_id` が claimed と一致することを要求する。bounds: `MAX_EVIDENCE_EVENTS=250000`、`MAX_BUNDLE_BYTES=64MiB`。 --- ## 9. 永続化(SQLite schema v4)と CLI 契約 ### 9.1 StateStore(schema v4) `APPLICATION_ID="PALW"`、`SCHEMA_VERSION=4`。`open` は parent dir 解決 + `SQLITE_OPEN_NOFOLLOW`、 `foreign_keys=ON`、`synchronous=FULL`、`journal_mode=WAL`、application/schema 照合、および `sqlite_master` の **DDL fingerprint**(SHA3-256 golden 照合)。`accept` は `BEGIN IMMEDIATE` で duplicate receipt / duplicate execution_nullifier / job-class 整合 / slot 充填を強制し、 `jobs`(PK `(network_id,job_nullifier)`、WITHOUT ROWID)と `receipts`(PK `receipt_id`、 `execution_nullifier` UNIQUE、`UNIQUE(network_id,job_nullifier,replica_slot)`、FK RESTRICT)へ transaction で insert する。復元経路 `restore_accepted` は read-only で既存 row を byte 照合する。 ### 9.2 `palw-metal-receipt`(発行 CLI) 必須: `--prompt-stdin`、`--audit-key-file`(**exact 32 raw bytes**、owner-only single-link `0400`/`0600`、output dir 外)、`--output-dir`(新規/空、owner-only `0700`)。任意: `--n-predict` (`1..=1024`、default 2)。prompt は UTF-8 非空 ≤1 MiB。run flow は §0 の通り。`canonical_source_patch` は `tools/CMakeLists.txt` と `src/models/qwen35moe.cpp` の modified、`tools/palw-observer/` の untracked のみを許容する(他は拒否 = fail-closed)。出力 artifact: | file | mode | 内容 | |---|---|---| | `.palw` | 0644 | 署名済み canonical receipt | | `.json` | 0644 | 公開 metadata(`misaka.palw.public-receipt.v2`、strict field) | | `.palw.bundle` | 0600 | XChaCha20 暗号化 bundle | | `palw-state.sqlite3` | 0600 | schema v4 state | | `.complete` | 0644 | `misaka.palw.receipt-set.v2` 完了 marker(**最後**に fsync 付きで作成) | ### 9.3 `palw-verify-bundle`(検証 CLI) 必須: `--receipt`/`--bundle`/`--public-json`/`--audit-key-file`/`--state-db`。artifact set の mode/owner/single-link/同一 private dir、完了 marker の binding、bounded read、`decode_strict`、 `validate_artifact_names`(receipt_id は bundle 由来)、`verify_bundle_and_restore`(§8 の復号・ 再検証・schema-v4 復元)、`verify_public_metadata`(公開 JSON を authenticated 値と field 単位で 照合)。正常時 `status=local_restored` / `trust_scope=embedded_local_snapshot`。この trust scope は bundle 内 registry snapshot による continuity を示すのみで、network authority の代替ではない。 --- ## 10. E2E 実測(reference receipt) Apple M1 Max(macOS Metal 4、arm64)。observer は load 時に qwen35moe/40/2048/248320 を検証し `offloaded 41/41 layers to GPU`。出力健全性: `The capital of France is` (`[760,6511,314,9338,369]`)→ ` Paris, a city renowned for its iconic`。 reference artifact は [`receipts/final-v7/`](../receipts/final-v7)、evidence 詳細は [`docs/evidence/metal-hybrid-qwen36-2026-07-19.md`](evidence/metal-hybrid-qwen36-2026-07-19.md)。 公開 receipt(`misaka.palw.public-receipt.v2`)の主要値: | field | 値 | |---|---| | `receipt_id` | `8e2dd34b7609d6d7aeec17bff485eb1ae4db31f66dd899a3c3a3e671656053f9` | | `verification_bundle_id` | `f5b8a2968c4ea266b2cbbabf351a171e0fa2581984eff63362de2640fcdcf9db` | | `cu_ruleset_id`(**v3 semantic**) | `43a5feef177b389f976361b22ac9d57da65815261282005bc78d1666a37870ce` | | `job_class` / `evidence_level` / `trace_evidence` | `self_local` / `gemm_traced` / **`metal_kernel`** | | `prefill_tokens` / `decode_tokens` | `5` / `2` | | `prefill_graph_steps` / `decode_graph_steps` | `5` / `1` | | `schedule_event_count`(= canonical_operations、commitment-only) | `13770` | | `trace_event_count`(= gemm_events) | `2466` | | `canonical_compute_units`(**v3 semantic、署名 commit**) | `41692` | | `semantic_compute_units_v3`(= canonical_compute_units) | `41692` | | `semantic_schedule.commitment` / `.expert_route_ops` | `e6fc731e…` / `80` | | `expert_route.root` / `.route_event_count`(実捕捉) | `5e4ea70b…` / `240` | | `mint.eligible` / `mint.weight` / `mint.mainnet_ready` | `false` / `0` / `false` | | `mint.ineligible_reasons` | Metal kernel-launch-bound / 非 network-anchored / 非 bonded の 3 件(generic+graph-fallback は解消) | | `model_profile_id` | `b6b83a274a889fc5dfa2044b9158111b3849f880a05eb7ebcf560d036515d472` | | `runtime_class_id` | `8f6f536118a72be70dab2028535c3bb95e3de9a3bac0e9b81cf78677ae902a9c` | | `artifacts.llama_commit` / `model_bytes` | `12127def…` / `23938321728` | privacy: prompt / prompt token IDs / 生成 token IDs / opening / signing key / owner salt は公開 `.palw` / `.json` に**含まれない**(全 9 flag が非公開/暗号化)。別 process の `palw-verify-bundle` が `status=local_restored`、`trust_scope=embedded_local_snapshot`、receipt_id/bundle_id 一致、 exit 0 で再検証済み。発行は実行ごとに OS CSPRNG で identity を生成するため receipt_id は再現しない。 --- ## 11. セキュリティ境界・正直な限界 - **この Receipt は mint-grade ではない。** 現行の `evidence_level=gemm_traced` / `trace_evidence=graph_fallback` は**強い計算証明ではない**。`runtime-palw/src/mint.rs` の `assess_mint_eligibility` が現行 runtime の全 receipt を `eligible=false, weight=0, class="local_self_consistent_v0_1"` と判定し、公開 JSON の `mint` ブロックに失格理由と共に 自己申告する(`palw-verify-bundle` が再構成して照合)。用途は「ローカル自己整合 receipt / testnet 計測 / Self-Local 非報酬」に限られる。mint-grade 化の是正計画は [`receipt-review-remediation.md`](receipt-review-remediation.md) を参照。 - Metal の graph-fallback sketch は **CUDA kernel trace ではない**。 - 単独ノードが発行する Receipt は、それだけでゼロ知識の計算証明ではない。PALW の不正耐性は runtime/model digest、署名、k=2 replica、future audit、canary、bond/slashing の組み合わせで 成立する。 - **`Generic` 演算は廃止済み(M3)。** hybrid の全 MoE/SSM op を忠実な canonical 演算へ写像 (`MUL_MAT_ID→ExpertGemm`、`ARGSORT→ExpertRoute`、`SSM_CONV→SsmConv`、`GATED_DELTA_NET→GatedDeltaNet`、 `L2_NORM→L2Norm`、`SUM_ROWS→Reduction`、`CONCAT→TensorCopy`、`UNARY/SCALE/DIV/CLAMP→Elementwise`)。 実機 receipt の schedule は `contains_generic_operation()==false`。 - **Semantic CU v3 は canonical commitment(M1、mainnet compute measure)。** `semantic_cu.rs` の `qwen36_semantic_compute_units(prefill, generated)` は CU を **observed graph からではなく pinned model 構造 + token 数**から算出(active-parameter FLOPs + causal attention 項、丸めは最後に 1 回)。 現行 receipt の `canonical_compute_units` は **この semantic 値(41692)を `cu_ruleset_id=v3` で署名 commit** する(v2 の graph 値ではない)。fragmentation-proof / backend 非依存で、verifier は `prefill_tokens`/`decode_tokens` から同値を再計算・照合。 - **canonical semantic schedule(M2、#7 構造半分)。** `semantic_schedule.rs` が pinned architecture + token 数から canonical op schedule と commitment を決定的に再生成し、公開 JSON `semantic_schedule` に束縛。verifier が独立再生成して照合。 - **実 MoE routing の捕捉(M4/#6)。** observer が `ffn_moe_topk`(I32)を post-compute で読み戻し、 per-token 選択 expert を封緘 bundle の `expert_route_root` へ commit、公開 JSON `expert_route` として 発行・再構成。実機で 240 route record を捕捉。 - **kernel-level trace(#5、M5、完了・実機検証済み)。** ggml-metal を計装して各 GEMM を実 Metal kernel dispatch + launch geometry へ束縛(200/200・411/411、0 anomaly)。`trace_evidence=metal_kernel` (graph-fallback から昇格)、`TraceEvidenceKind/TraceCapability::MetalKernel` を新設し verifier が照合。 **launch-geometry 束縛であり CUDA V3 相当の intra-kernel accumulator proof ではない**ため `KernelSketch` とは別 kind で commit(over-claim しない)。mint 失格理由は「Metal kernel-launch-bound sketch, not an intra-kernel accumulator proof」に置換(mint 不適格のまま)。accumulator-level は Metal shader 計装が残件。 - **mainnet-readiness。** `assess_mainnet_readiness` が compute / network / hardware / model-Genesis の 全 gate を列挙し、外部 gate を Met と偽らない。現状 `mint.mainnet_ready=false`。mainnet 報酬適格性は deployed network authority / global nullifier / epoch beacon / bond authority / 複数世代実機 / 公式モデル Genesis を要し、この repository の完了範囲外である(remediation の外部ブロック項参照)。 - 未達 gate R13/R21/R23/R24/R26/R27/R32/R35 は [`requirements.md`](requirements.md) の通り `In progress`。本移行はこれらの production 完了を意味しない。 ### 既知の drift とテスト網羅(2026-07-19 時点) - **hybrid adapter の専用 unit test** は fixture test を追加済み(`hybrid_generic_op_class` 表、 mapping id 識別、MoE/SSM op 写像、unknown op fail-closed、q6_K trace、dense header 拒否)。 CU fragmentation invariant や公式 Transformers との differential 等の残りは [`receipt-review-remediation.md`](receipt-review-remediation.md) の follow-up。 - `validate_hybrid_pin` は dense と異なり `base_artifact`/`inference_artifact` の repository 文字列を pin せず、deserialize する MoE/linear-attention の追加 dim(`linear_*`、 `shared_expert_intermediate_size`、`full_attention_interval`、`partial_rotary_factor_millionths`) を committ manifest には使わない(`deny_unknown_fields` 受理のためだけに存在)。 --- ## 12. 再現手順 ```sh # 1. 導入(GGUF は Ollama registry、base metadata は HF) ./scripts/install.sh # 2. Rust gate(MSRV 1.85) cd runtime-palw cargo +1.85.0 fmt --all -- --check cargo +1.85.0 clippy --locked --all-targets -- -D warnings cargo +1.85.0 test --locked --all-targets cargo +1.85.0 build --release --locked --bin palw-metal-receipt --bin palw-verify-bundle cd .. # 3. Receipt 発行(audit key は 32-byte raw、output dir 外に一度だけ作成) OUT="receipts/manual-$(date +%Y%m%d-%H%M%S)" AUDIT_KEY="$HOME/.config/misaka-palw/audit-keys/local-audit.key" install -d -m 700 "$(dirname "$AUDIT_KEY")"; test ! -e "$AUDIT_KEY" (umask 077 && /usr/bin/openssl rand 32 > "$AUDIT_KEY"); chmod 600 "$AUDIT_KEY" runtime-palw/target/release/palw-metal-receipt \ --prompt-stdin --audit-key-file "$AUDIT_KEY" --output-dir "$OUT" --n-predict 2 \ < /path/to/private-prompt.txt # 4. 別 process 検証 RECEIPT="$(find "$OUT" -maxdepth 1 -type f -name '*.palw' -print)"; ID="$(basename "$RECEIPT" .palw)" runtime-palw/target/release/palw-verify-bundle \ --receipt "$OUT/$ID.palw" --bundle "$OUT/$ID.palw.bundle" \ --public-json "$OUT/$ID.json" --audit-key-file "$AUDIT_KEY" \ --state-db "$OUT/palw-state.sqlite3" # 期待: status=local_restored / trust_scope=embedded_local_snapshot / exit 0 ``` GGUF の SHA-256 を発行前後に 4 回計算するため、1 回の発行は約 5 分要する(24GB × 4)。 これは hang ではない。