diff --git a/.claude/hooks/block-bulk-git-add.sh b/.claude/hooks/block-bulk-git-add.sh index 6bb35f8..f167fb0 100644 --- a/.claude/hooks/block-bulk-git-add.sh +++ b/.claude/hooks/block-bulk-git-add.sh @@ -2,10 +2,20 @@ # PreToolUse hook: `git add -A` / `git add .` を拒否する。 # # このリポジトリは build/、bin/、obj/、artifacts/ を .gitignore しているが、 -# 一括 add はそれ以外の意図しないファイル(生成物、一時的なデバッグ変更、 -# 別作業の途中経過)まで巻き込む。M0 では全 8 タスクのディスパッチで +# 一括 add はそれ以外の意図しないファイル(別作業の途中経過など)まで +# 巻き込む。M0 では全 8 タスクのディスパッチで # 「変更したパスだけを個別に stage せよ」と手で書き続けた。それを機械化する。 # +# **この hook が止めるのは `-A` / `--all` / `--no-ignore-removal` / `.` / +# `:/` / `:` の完全一致だけである。`git add -u` と `git commit -a` は通す。** +# 理由と実装をずらさないために明記しておく —— 以前この冒頭は「生成物、 +# 一時的なデバッグ変更まで巻き込む」と書いていたが、**その 2 つを正確に +# stage する動詞こそ `-u` であり、実装はそれを 1 度も見ていなかった。** +# 分担は意図的である: 生成物が履歴に入るのを止めるのは +# check-staged-generated-file.sh で、そちらは commit の時点で index の +# 結果を見る(動詞を 1 つずつ塞ぐと、塞がれていない動詞に移るだけである +# ——その判断は同ファイルの冒頭が書いている)。 +# # 実行系について: このフックは PowerShell ではなく sh で書いてある。 # 計測したところ、この環境では pwsh の起動に約 3.3 秒、python に約 2.4 秒 # かかるのに対し sh は約 0.12 秒だった。フックは毎回のツール呼び出しで走るため、 diff --git a/.claude/hooks/check-platform-list-drift.sh b/.claude/hooks/check-platform-list-drift.sh index c9feb46..dcc4b33 100644 --- a/.claude/hooks/check-platform-list-drift.sh +++ b/.claude/hooks/check-platform-list-drift.sh @@ -19,8 +19,12 @@ # # 述語: 正本の 3 件以上を名指ししているファイルは「全件の一覧」と見なし、 # 全件を要求する。**リポジトリ全体に当てて誤検出 0 を実測してある** -# (6 ファイルが該当し、6 つとも 5/5)。3 件未満のファイルは、 -# 意図して一部だけを扱っている(例: Linux の移植性検査)ので対象外。 +# (2026-09-10 に測り直し: 6 ファイルが該当し、6 つとも 6/6。 +# **実測日を書くのは、この値が platform が増えるたびに古くなるからである** +# ——「6 つとも 5/5」と書いてあった頃は M6 で platform が 6 になった時点で +# 古く、実装は正しく動いていたのに記載だけがずれていた)。3 件未満の +# ファイルは、意図して一部だけを扱っている(例: Linux の移植性検査)ので +# 対象外。 # # **見えないもの: 同じファイルの中の、2 つ目以降の一覧。** 判定はファイル # 単位なので、正しい 5 件の一覧と古い 3 件の一覧が同居していると通る。 diff --git a/.claude/hooks/check-unityengine-leak.sh b/.claude/hooks/check-unityengine-leak.sh index dfe79ca..532d9c2 100644 --- a/.claude/hooks/check-unityengine-leak.sh +++ b/.claude/hooks/check-unityengine-leak.sh @@ -1,7 +1,8 @@ #!/usr/bin/env sh -# PostToolUse hook: Runtime/Interop と Runtime/Core への UnityEngine 混入を検出する。 +# PostToolUse hook: UnityEngine 非依存であるべき Runtime のフォルダへの +# UnityEngine 混入を検出する。 # -# この 2 フォルダが UnityEngine に依存しないことが L3 レーンの前提である。 +# それらのフォルダが UnityEngine に依存しないことが L3 レーンの前提である。 # 依存しない限り、同じ .cs を netstandard2.1 の shim としてコンパイルでき、 # Unity を起動せずに素の .NET 上で P/Invoke を検証できる(約 20 秒)。 # UnityEngine が入った瞬間にその高速レーンは成立しなくなり、 @@ -9,6 +10,24 @@ # # UnityEngine 依存コードは Runtime/UnityIntegration/(別 asmdef)に置く。 # +# **対象フォルダの一覧を写さない。正本から読む。** +# 正本は tests/Managed/CvUnity.Runtime.Shim/CvUnity.Runtime.Shim.csproj の +# 要素のうち、**Include が $(OcvuPackageRuntime) を根に持つもの** +# である —— shim がコンパイルするフォルダが、そのまま「UnityEngine を +# 参照してはならない」フォルダの定義だからである。 +# **「csproj の を全部読む」ではない。** リテラルの相対パスで +# 同じフォルダを指す書き方は対象外で、そのフォルダは検査されない。 +# 何を除いているかと、その理由は下の抽出のところに書いてある。 +# **「行」ではなく「要素」を、合計ではなく要素ごとに見る。** そこも同じ。 +# +# 写していた頃の壊れ方(実測、2026-09-10): M7c が profile の分離で +# Runtime/Interop.Dnn と Runtime/Dnn を足し、shim の csproj は 4 フォルダを +# コンパイルするようになったのに、この hook は 2 フォルダのままだった。 +# 4 つに同じ probe を当てると Core と Interop だけが検知され、 +# **Interop.Dnn と Dnn は素通りした。** どちらも asmdef が +# noEngineReferences: true を宣言しており、書けば shim のビルドが落ちる。 +# profile が 1 つ増えれば同じ形でまた 2 フォルダ増えるので、写す形はやめた。 +# # 最終的な強制は shim のビルド(tests/Managed/CvUnity.Runtime.Shim)が行う。 # このフックはビルドを待たずに編集直後へ差し戻すための早期検出であって、 # 権威ではない。ここを通っても shim が落ちることはある。 @@ -23,10 +42,15 @@ set -u payload=$(cat) [ -n "$payload" ] || exit 0 -# 速い経路: 対象フォルダのパスを含まないなら jq を起動せず抜ける。 +# 速い経路: Runtime 配下のパスを含まないなら jq も csproj の読み取りも起こさない。 # JSON 内では Windows のパス区切りが \\ にエスケープされるため両方見る。 +# +# **ここでフォルダ名まで絞らない。** 絞ると一覧を写すことになり、上に書いた +# 壊れ方(新しいフォルダを黙って素通しする)がこの 1 行に戻ってくる。 +# Runtime/ で切るだけでも、このリポジトリの編集の大半(native/ tools/ docs/ +# tests/ .github/)はここで抜ける。 case "$payload" in - *"Runtime/Interop"* | *"Runtime/Core"* | *'Runtime\\Interop'* | *'Runtime\\Core'*) ;; + *"Runtime/"* | *'Runtime\\'*) ;; *) exit 0 ;; esac @@ -40,7 +64,149 @@ case "$file" in *) exit 0 ;; esac -printf '%s' "$file" | grep -qE '[/\\]Runtime[/\\](Interop|Core)[/\\]' || exit 0 +# 正本の場所を cwd に依存させない。hook は任意の cwd で呼ばれうる。 +# repo root で呼ばれるのが普通なので、まず相対で当たるかを見て、 +# 外れたときだけ git を起こす(速い経路の一部である)。 +shim_csproj="tests/Managed/CvUnity.Runtime.Shim/CvUnity.Runtime.Shim.csproj" +if [ ! -f "$shim_csproj" ]; then + repo_root=$(git rev-parse --show-toplevel 2>/dev/null || true) + shim_csproj="${repo_root:-.}/tests/Managed/CvUnity.Runtime.Shim/CvUnity.Runtime.Shim.csproj" +fi + +# -------------------------------------------------------------------------- +# csproj から「shim がコンパイルするフォルダ」を読む。 +# +# **読むのは 要素のうち、Include が $(OcvuPackageRuntime) を根に +# 持つものだけである。** これは「csproj の を全部読む」より狭い。 +# **除いているもの**: リテラルの相対パスで同じフォルダを指す書き方 +# ()は +# 下の grep -F で落ち、そのフォルダはこの hook の対象にならない。 +# **意図してそうしてある**(プロパティ経由が現在の唯一の書き方で、 +# リテラル解決はこの hook の仕事ではない)が、**書き方を変えるなら +# ここも変える必要がある。** 網羅していると読ませないために明記しておく。 +# +# **要素単位で切る。行単位で読まない。** 改行を空白に潰してから '<' で切ると、 +# 1 行に複数の要素が並んでいても、属性が次の行にあっても、1 要素 1 行になる。 +# XML コメントは範囲指定で落とす('<' で切ると開始が `!--` で始まる行になり、 +# 終了は `-->` を含む行になる)。実測: コメントアウトした を、 +# 以前は生きた行として数えていた。 +# +# ** だけを見る。** 以前は「$(OcvuPackageRuntime) を含む行」を拾って +# おり、コメントのほうは「 行を読む」と書いていた。実測: +# を +# 足すと Runtime/UnityIntegration を対象と誤認し、負の対照が壊れた。 +# block-bulk-git-add.sh で直したのと同じ「理由と実装のずれ」である。 +compile_elements="" +[ -f "$shim_csproj" ] && compile_elements=$( + tr '\n' ' ' < "$shim_csproj" | + tr '<' '\n' | + sed '/^[[:space:]]*!--/,/-->/d' | + grep -E '^[[:space:]]*Compile[[:space:]]' | + grep -F '$(OcvuPackageRuntime)' +) + +# **黙って素通ししない。** 読めないまま通すと、この hook は何も見なくなり、 +# しかも指摘が出ないので気づけない(check-platform-list-drift.sh と同じ判断)。 +# +# **そして、合計で守らない。要素ごとに守る。** +# ここは 2 度作り直している。1 度目は「N 件以上読めたか」(`-lt 2`)で、 +# **4 フォルダのうち 2 つしか読めない誤読を、2 件読めているという理由で +# 通した。** 2 度目は合計 3 本(要素数・パス数・フォルダ数)の突き合わせで、 +# **合計は釣り合わせられた** —— 実測: 3 パスを持つ要素 1 つと、 +# `Include =` に空白があって 1 つも読めない要素 1 つを並べると +# 要素 2 / パス 3 / フォルダ 3 で 3 本とも成立し、**Runtime/Dnn が黙って +# 無防備になった。** 閾値より 1 段難しいだけで、形は同じである —— +# **誤った読みが門を満たす。** +# +# **合計は集約であり、情報は要素の側にある。** だから +# **「変数を根に持つ 1 つにつき、その要素の Include から +# フォルダがちょうど同じ数だけ取れたか」**を要素ごとに見る。 +# **この 1 本が、以前の合計 3 本のうち 2 本を吸収した** —— +# 「パスを 1 つ落とした」も「要素ごと落とした」も、要素の中で見れば +# 同じ 1 つの不一致になる。残したのは下限(要素が 1 つも無ければ +# 読めていない)だけで、そちらは要素ごとの検査では原理的に代替できない +# (要素が 0 個なら、要素ごとの検査は空虚に真になる)。 +# +# **Include の書き方は MSBuild に合わせる**: 引用符は二重・単重の両方、 +# `=` の前後の空白、そして **1 つの Include に ';' 区切りで複数のパス**。 +# **Remove= / Update= は Include を持たないので、変数を名指ししていれば +# ここで声を上げて止まる。** それは意図した振る舞いである(対象フォルダを +# 減らす操作を、黙って無視するより止めたい)—— 実際に使うなら、この抽出を +# 拡張すること。**解析器の欠陥ではない。** +elem_count=$(printf '%s\n' "$compile_elements" | grep -c '[^[:space:]]' || true) +folders="" +bad_element="" +bad_detail="" + +# here-doc を done に付けると、代入がサブシェルに閉じ込められない +# (check-platform-list-drift.sh と同じ形)。 +while IFS= read -r elem; do + [ -n "$elem" ] || continue + [ -z "$bad_element" ] || continue + + e_paths=$( + { + printf '%s\n' "$elem" | + sed -n 's/.*[Ii]nclude[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' + printf '%s\n' "$elem" | + sed -n "s/.*[Ii]nclude[[:space:]]*=[[:space:]]*'\([^']*\)'.*/\1/p" + } | tr ';' '\n' | grep '[^[:space:]]' || true + ) + e_refs=$(printf '%s\n' "$e_paths" | grep -cF '$(OcvuPackageRuntime)' || true) + e_folders=$( + printf '%s\n' "$e_paths" | + sed -n 's/^[[:space:]]*$(OcvuPackageRuntime)[\\/]\([^\\/]*\)[\\/].*/\1/p' + ) + e_count=$(printf '%s\n' "$e_folders" | grep -c '[^[:space:]]' || true) + + if [ "$e_refs" -lt 1 ] || [ "$e_count" -ne "$e_refs" ]; then + bad_element=$(printf '%s' "$elem" | cut -c1-160) + bad_detail="変数を根に持つパス ${e_refs} / 取り出せたフォルダ ${e_count}" + continue + fi + + folders=$(printf '%s\n%s' "$folders" "$e_folders") +done < 104857600`)| 加えて `tools/tests/OpenCvConfig.Tests.ps1` と `tools/tests/PackageRelease.Tests.ps1` が、上のいくつかの一致と数を見ている。**そちらの数も一緒に増える。** @@ -85,14 +87,17 @@ OpenUPM の API を叩いて初めて気づき、**3 platform のまま残って **外部リポジトリへの PR なので、人の確認を取ってから出すこと。** **M6 で 4 つ目が出た(2026-09-03)。同じファイルの中に振り分けが 2 つある形である。** -`.github/workflows/release.yml` は「未知の platform は失敗させる」門を **2 箇所**持つ -(linkage の検査と移植性の検査)。**M6 では後者にだけ web-wasm を足し、前者に -足し忘れた** —— ローカルの全レーンは緑のまま、CI の `Package web-wasm` だけが落ち、 +`.github/workflows/release.yml` は「未知の platform は失敗させる」門を **3 箇所**持つ +(linkage の検査・移植性の検査・**公開面の検査**。3 つ目は M7b の +`verify-exported-symbols.ps1` が足した)。**M6 の時点では 2 箇所で、後者にだけ +web-wasm を足し、前者に足し忘れた** —— ローカルの全レーンは緑のまま、CI の +`Package web-wasm` だけが落ち、 `Assemble` がそれに連鎖した。**`pack-upm-tarball.ps1` の switch、 `PackageRelease.Tests.ps1` の 3 つ目の一覧に続く 3 度目である。** `tools/tests/OpenCvConfig.Tests.ps1` に検査を足してあり、**一覧を持たない** —— `unknown platform '` を持つ step を全部見つけ、正本から読んだ全 platform が -それぞれに現れることだけを見る。 +それぞれに現れることだけを見る。**その設計が働いた実例が M7b である** —— +3 つ目の門が増えたとき、検査側は 1 行も変えずに新しい門を見るようになった。 **hook が見えない一覧が、まだ 3 つある。** `check-platform-list-drift.sh` は **binary の相対パス**で判定するので、次の形は構造的に見えない: @@ -116,10 +121,13 @@ OpenUPM の API を叩いて初めて気づき、**3 platform のまま残って 実装し、未知は既定で落とす」形は近くに `PLATFORM_LIST_OK:` と理由を書いて許す。 **理由を書かせるのが目的**で、黙って除外できる形にはしていない。 -**数を信用しない。** この表の「17」も、次に platform を足す人にとっては古い。 +**数を信用しない。この skill から数を落としたのはそのためである** —— +以前はここに「17 箇所」と書いてあり、**M7 で 2 つ増えたときも 17 のままだった** +(表の行は増やしたのに、数だけが取り残された)。**表の行数が正本である。** `git grep -c -E '<既存の platform 名>'` で数え直すこと。**文書に書かれた数は、 足したときに一緒に増えるとは限らない** —— roadmap は長らく「2 か所」と書いて -いたが実際は 3 か所で、M4 で判明した。 +いたが実際は 3 か所で、M4 で判明した。**`add-abi-function` からこの数を写して +いた記述も、同じ日に落とした。** ## 罠 @@ -233,6 +241,16 @@ Android の OpenCV は `libcpufeatures.a`(NDK 由来)を引く。 `THIRD_PARTY_NOTICES.md` に**全文**を足す。名前を allowlist に足して 緑に戻すだけで済ませない —— **利用者が読む文書は何も赤くならない。** +**逆向きの限界がある: allowlist が見ているのは、artifact の中に独立した +ライブラリとして現れる依存だけである。** 別のライブラリの中へ静的に取り込まれた +third-party は、allowlist にもライセンスディレクトリにも**現れない。** +M7c で実測した —— `dnn` が引き込むもののうち `protobuf` は捕まったが、 +**MLAS と ONNX Runtime は `libopencv_dnn.a` の一部なので、allowlist には +何も見えなかった**(roadmap の `### M7c の判定`)。**「allowlist が緑だから +新しい third-party は無い」とは言えない。** module を足す話として出たが、 +platform を足すときも同じ形で見落とす —— **新しい platform の artifact に +何が入っているかは、allowlist の外側でも 1 度は自分で見ること。** + ### 6. 「N 件」の一覧は置いていかれる 3 platform 用に書かれた一覧が、5 platform になっても 3 件のまま残る。 @@ -261,6 +279,19 @@ M4 では `Plugin ios-arm64` がこれで落ち、**artifact は 20 分後に正 当てたいのはこちらの `.so` なので、**toolchain ファイル側**に、しかも **NDK の toolchain を include した後に**追記する(先に書くと上書きされる)。 +**M7c が対になる半分を実測した。** 構成ハッシュ(`Get-OpenCvConfigHash`)は +**`tools/opencv-config.psd1` の内容しか見ない。** したがって +`cmake/toolchains/*.cmake` を変えてもハッシュは動かず、**復元済みの OpenCV +artifact がそのまま使われて、書いた flag は OpenCV 側に効かない。** +置き場所は目的で決まる: + +| 変えたいもの | 置き場所 | 帰結 | +| --- | --- | --- | +| **OpenCV** のビルド | `tools/opencv-config.psd1` | 構成ハッシュが変わり、**全 platform 分**を作り直す | +| **こちらの** binary のコンパイル / リンク | `cmake/toolchains/.cmake` または `native/CMakeLists.txt` | ハッシュは動かない。復元済み artifact はそのまま使われる | + +**「flag をどこに書くか」は好みではなく、何を作り直すかの選択である。** + ## OpenCV は CI にビルドさせる。手元で回さない **これは新しい platform を足すときに最も破りたくなる規則である。** diff --git a/.claude/skills/add-abi-function/SKILL.md b/.claude/skills/add-abi-function/SKILL.md index 76c7c87..8d11581 100644 --- a/.claude/skills/add-abi-function/SKILL.md +++ b/.claude/skills/add-abi-function/SKILL.md @@ -49,6 +49,22 @@ ABI 関数を 1 本足す作業は 5〜7 ファイルにまたがる。順序を **`false` を選ぶのは最後の手段である**: IL2CPP の stripping が消せるのは 呼ばれない宣言なので、**呼ばない宣言は消えても誰も気づかない。** +**profile 付きの module では `reachable: true` だけでは同じ穴が開く。** +既定 profile の到達性クラスは `AbiSurfaceTests.cs` / `AbiSurfacePlayerTests.cs` / +`WebSmokeRunner.cs` の 3 箇所から呼ばれるが、**profile 版は呼び出し元を自分で +作らなければならない。** M7c は `AbiReachabilityChecksDnn.CallEveryEntryPoint()` を +生成したのに**呼び出し元が 1 つも無いまま**レビューまで気づかれなかった —— +**生成されたのに 1 度も呼ばれない = stripping の対象そのもの**で、「検査が無い」 +より悪い(roadmap の `### M7c の判定`)。閉じた形は +`tests/UnityProject/Assets/Tests/PlayMode/AbiSurfaceDnnPlayerTests.cs` +(`#if OCVU_PROFILE_DNN` で自分を守り、PlayMode の asmdef に +`CvUnity.Tests.Shared.` への参照を足す)である。 + +**そしてその確認は手動である。** `OCVU_PROFILE_DNN` を立てる workflow も +`dev.ps1` のレーンも**存在しない**(2026-09-10 に `.github/` と `tools/` を +grep して 0 件)。**手で define を立てて `test-unity-player` を 1 回走らせ、 +日付つきで記録すること** —— **CI は自動では再検証しない。** + **`reachable: true` が証明するのは「宣言が stripping を生き延びた」ことだけである。** 生成される到達性テストは全引数を `0` / `0UL` / `0.0` / `null` / `out _` で渡し、 status を捨てる —— **値が正しいかは見ていないし、配列の marshalling @@ -60,6 +76,11 @@ status を捨てる —— **値が正しいかは見ていないし、配列の > **Unity の中で値まで見る検査を > `tests/UnityProject/Assets/Tests/Shared/AbiSurfaceChecks.cs` に足し、 > EditMode と PlayMode の両入口に配線する。** +> +> **profile 付きの module なら、共有本体も入口も profile 側にある** —— +> `tests/UnityProject/Assets/Tests/Shared./` と +> `AbiSurfacePlayerTests.cs` である。**既定 profile の入口に足しても +> コンパイルされない**(`defineConstraints` で切れている)。 **目視で確かめない。C# の入口名で機械的に突き合わせること。** 2026-09 の API 拡張で 26 本を足したとき、**4 本(`Canny` / `InsertChannel` / @@ -70,11 +91,54 @@ status を捨てる —— **値が正しいかは見ていないし、配列の 配線の漏れ自体は `EveryCheckInTheSharedBodyIsWiredIntoThisEntryPoint` が 名指しで落とす(`prove-a-check-works` skill の §5)。 -**逆向きの検査が見ているのは `native/src/**/*.cpp` だけである。** -`tools/tests/BindingGenerator.Tests.ps1` は、そこにある -`extern "C"` の `ocvu_*` を全部拾って spec に無いものを落とす。 -**他所に定義を置くと網に入らない** —— 「一覧を持つ場所」がこれで 1 つ増えた。 -`native/src` の外に ABI の実装を置くなら、この検査の走査範囲も一緒に広げること。 +**逆向きの検査は 2 本ある。ソースを読む方と、binary を読む方である。** + +1. **ソースを読む(ローカル・速い)。** `tools/tests/BindingGenerator.Tests.ps1` が + `native/src/**/*.cpp` の `extern "C"` の `ocvu_*` を全部拾い、spec に無いものを + 落とす。**見ているのはそのディレクトリだけ**なので、**他所に定義を置くと網に + 入らない** —— 「一覧を持つ場所」がこれで 1 つ増えた。`native/src` の外に ABI の + 実装を置くなら、この検査の走査範囲も一緒に広げること。 +2. **binary を読む(CI のみ・実物)。** `tools/verify-exported-symbols.ps1`(M7b)が + **出来上がった binary の export 面**を spec と過不足なく照合する —— + spec にあるのに export されていない関数も、export されているのに spec に無い + 関数も落ちる。走るのは `ci-native.yml` の desktop 3 job(開発用の binary)と + `release.yml`(**配る package に置かれた実物**)で、**後者は最終レビューで + 足すまで配る経路に 1 度も掛かっていなかった**(docstring は「配布 binary の + 公開面」と主張していた。`prove-a-check-works` skill の §2 を参照)。 + `native/modules.cmake` の `OCVU_MODULES` を絞ると、外した module の関数が + 実際にここで「不足」として捕まる。 + +**この 2 本は代替にならない。** 1 は実装漏れを編集直後に捕まえるが binary を +見ておらず、2 は実物を見るが CI でしか走らない。 + +### profile を持つ module に足すとき(M7b で機構、M7c で実物) + +**spec の `profile` は既定が `standard` で、そこが違うと生成物の行き先・クラス名・ +assembly がまとめて変わる。** 現在それに当たるのは `bindings/spec/dnn.json` +(`"profile": "dnn"`)だけだが、**module 名も profile 名もここに写さない** —— +正本は spec のファイル自身と `docs/abi-ownership-and-versioning.md` §4 である。 + +| | 既定(`standard`) | 非既定(例: `dnn`) | +| --- | --- | --- | +| C# の出力先 | `Runtime/Interop/` | `Runtime/Interop./` | +| クラス名 | `NativeMethods` | `NativeMethods` | +| assembly | `CvUnity.Interop` | `CvUnity.Interop.`(`defineConstraints` で切れる) | +| 到達性テスト | `Tests/Shared/AbiReachabilityChecks.g.cs` | `Tests/Shared./AbiReachabilityChecks..g.cs` | +| 呼び出し元 | 既にある 3 箇所 | **自分で作る**(上の `reachable` の節) | + +**`LibraryName` は生成器が `#if` 分岐ごと複製する。** 手書きの +`Runtime/Interop/NativeMethods.cs` にある `internal const` は**別 assembly・別型 +からは見えない**ので、複製しないと生成物が `CS0103` でコンパイルできない —— +**M7b はこの穴を通して欠陥を 1 件入れた**(検査が見ていたのは出力の文字列と +パスだけで、**生成物を 1 度もコンパイルしていなかった**)。写しが 2 つある形は +`Ocvu.Generator.Tests` の `ProfileTests` が読み比べて守っている。 + +**native 側は profile で分かれない。** `native/modules.cmake` の +`OCVU_ALL_MODULES` には profile 付きの module も入っており、**配る binary には +その関数が必ず入っている。** 切っているのは C# の assembly だけである —— +**「binary に入っている」と「C# 側がコンパイルする」は別**で、これは M3.5 で +踏んだ「OpenCV に入っている」と「このプラグインがリンクしている」の +取り違えと同じ形が 1 段上の層で再現したものである。 ### 生成物を増やすなら、先頭 5 行で名乗らせる @@ -94,6 +158,15 @@ emitter を足して新しい生成物を出すときは、**その出力の先 静かに漏れる —— M5 でその形を踏んだ(生成物 10 個のうち名指しで守られていたのは 2 個だけで、**到達性テストの配線を外しても検査は全部 PASS した**)。 +**この設計が働いた実例が 2 つある。** M5 の module 追加(`objdetect` / `features`)で +生成物が 4 つ増えたときと、**M7c で `dnn` の生成物 3 つ** +(`native/include/ocvu/dnn.h` / `Runtime/Interop.Dnn/NativeMethods.Dnn.g.cs` / +`Tests/Shared.Dnn/AbiReachabilityChecks.Dnn.g.cs`)が増えたとき —— +**どちらも hook を 1 行も触っていないのに検知範囲に入った。** +**対照的に、一覧を写していた `check-unityengine-leak.sh` は M7c で守備範囲が +2 → 4 フォルダになったのに 2 のままで、2 つのフォルダを素通ししていた** +(2026-09-10 に正本から読む形へ直した)。 + **名乗らない生成物は、どちらの網にも入らない。** これが現在の限界である。 **もう 1 つ限界がある。** hook は速い経路として `.h` / `.cs` / `.md` を含む payload @@ -133,6 +206,12 @@ OpenCV を作り直す(実測: `calib` を足したとき `4785d98e9aad` → ` `calib` は `stereo` を引き、**4 platform とも最初のビルドがここで落ちた。** **これは検査が働いた例であって、回避すべき障害ではない** —— 引き込まれたものを 確かめてから明示的に足す。 + **ただし allowlist が見ているのは、artifact の中に独立したライブラリとして + 現れる依存だけである。** 別のライブラリの中へ静的に取り込まれた third-party は、 + allowlist にもライセンスディレクトリにも現れない —— M7c の `dnn` で実測した: + `protobuf` は捕まったが、**MLAS と ONNX Runtime は `libopencv_dnn.a` の一部 + なので、allowlist には何も見えなかった**(roadmap の `### M7c の判定`)。 + **「allowlist が緑だから新しい third-party は無い」とは言えない。** - **`THIRD_PARTY_NOTICES.md` の module 列挙**(配布物に同梱される法的通知文書) - **`README.md` の「which OpenCV modules this plugin links」** - **`tools/tests/OpenCvConfig.Tests.ps1` の module 検査**(spec と両方向に突き合わせる) @@ -216,12 +295,20 @@ C# の XML doc・API 対応表の「内容」列に同時に出る** —— 3 直前にも当たるので、`Regex.IsMatch` だけでは末尾の改行を通してしまう (`prove-a-check-works` skill に実例がある)。 -**module ごとにファイルが分かれている。** **module 名をここに写さない** —— +**module ごとにファイルが分かれている。** **module 名も数もここに写さない** —— `ls bindings/spec/*.json` が正本である(写すと module を 1 つ足すたびに -この行だけが嘘になる。実際 6 -> 9 になった)。既存のどれにも入らないなら新しい -`.json` を作る —— そのとき生成されるヘッダも `native/include/ocvu/.h` -として増え、`native/include/opencv_unity_native.h` の `#include` 一覧に手で 1 行足す -必要がある(**その 1 行だけは生成物ではない**)。 +この行だけが嘘になる。**実際に何度も増えている**)。既存のどれにも入らないなら +新しい `.json` を作る —— **そのとき手で書く箇所が 2 つある。どちらも +生成物ではない:** + +1. **`native/include/opencv_unity_native.h` の `#include` に 1 行。** + 生成されるヘッダが `native/include/ocvu/.h` として増えるが、 + それを取り込む行だけは生成されない。 +2. **`native/modules.cmake` に 2 行** —— `set(OCVU_MODULE_ src/....cpp)` と、 + `OCVU_ALL_MODULES` への追加。**足さないと 2 箇所が落ちる**: + `native/CMakeLists.txt` が `module '' が modules.cmake に無い` で + `FATAL_ERROR`(configure が進まない)、`tools/tests/BindingGenerator.Tests.ps1` が + `OCVU_ALL_MODULES` と spec のファイル名の一致を要求して速いレーンで落ちる。 **生成される C# のファイル名は module 名が大文字である**(`NativeMethods.Objdetect.g.cs`)。 Windows は大文字小文字を区別しないので小文字で `git add` しても通ってしまうが、 @@ -237,8 +324,16 @@ status の唯一の定義元で、列挙子と実行時テーブルの両方が ### 3. 実装する `native/src/` の適切な `.cpp` に書き、新規ファイルなら -`native/CMakeLists.txt` の `OCVU_SOURCES` に足す。SHARED と STATIC の両方の -ターゲットがこのリストを共有しているので、1 箇所で済む。 +**`native/modules.cmake` の `OCVU_MODULE_` に足す。** +**M7b で `OCVU_SOURCES` は手で書く一覧ではなくなった** —— `native/CMakeLists.txt` +は `OCVU_MODULES` を走査して `OCVU_MODULE_` を連結するだけで、 +**そこに一覧は無い。** SHARED と STATIC の両方のターゲットが組み立て結果を +共有するので、足すのは 1 箇所で済む。 + +**どの module に置くかは「どの module の一部か」ではなく「どれを外したら +一緒に消えるべきか」で決める**(`native/modules.cmake` の冒頭がその規則を +書いている)。実例: `src/ocvu_calibration.cpp` は `objdetect` / `calib` / +`imgproc` の 3 module に関数を出すが、**最も外せない `calib` に置いてある。** **本体は例外バリアで囲む:** @@ -495,18 +590,23 @@ Windows の CI でも出ないので、**リークするコードは PR を出 `generate` を通しても、名前を書くまで速いレーンが赤いままである (2026-09-05 に実測。26 本を足す作業で踏んだ)。 -**生成物も一緒にコミットする。** `native/include/ocvu/*.h`、 -`Runtime/Interop/NativeMethods..g.cs`(**`.meta` も**)、 -`tests/UnityProject/Assets/Tests/Shared/AbiReachabilityChecks.g.cs`、 -`docs/api-map.md` は git が追跡している。spec だけ入れて生成物を入れないと、 -**CI の `dev.ps1 test` が全 platform で落ちる。** +**生成物も一緒にコミットする。** **パスを覚えず、生成器に申告させること** —— +`dotnet run --project bindings/generator/Ocvu.Generator -- --repo-root . --list-outputs` +が自分の出力を全部挙げる(`tools/tests/BindingGenerator.Tests.ps1` が +同じ一覧を使っている)。既定 profile なら +`native/include/ocvu/*.h`、`Runtime/Interop/NativeMethods..g.cs`、 +`tests/UnityProject/Assets/Tests/Shared/AbiReachabilityChecks.g.cs`、`docs/api-map.md` +だが、**非既定 profile では出力先が変わる**(`Runtime/Interop./` と +`tests/UnityProject/Assets/Tests/Shared./`)。いずれも git が追跡しており、 +**`.meta` も同じ数だけ要る**(生成器は `.meta` を作らない)。spec だけ入れて +生成物を入れないと、**CI の `dev.ps1 test` が全 platform で落ちる。** ## よくある取りこぼし | 症状 | 原因 | | --- | --- | | `StatusCodeSyncTests` が赤い | `OCVU_STATUS_LIST` に足したが `CvStatus.cs` に足していない | -| リンクエラー(テストのみ) | 新規 `.cpp` を `OCVU_SOURCES` に足していない | +| リンクエラー(テストのみ) | 新規 `.cpp` を **`native/modules.cmake` の `OCVU_MODULE_`** に足していない(**M7b より前は `native/CMakeLists.txt` の `OCVU_SOURCES` だった。そこにもう一覧は無い**) | | L1 は緑だが L3 で `EntryPointNotFoundException` | spec に書いて生成したが `.cpp` に定義がない、または名前が違う | | `dev.ps1 test` が「生成物が spec と食い違っています」で落ちる | spec を直して `dev.ps1 generate` を実行していない(生成物をコミットするところまでが 1 手である)か、生成物を手で編集した | | `BindingGenerator.Tests.ps1` の「every extern C ocvu_* in native/src is declared in the spec」が落ちる | 実装だけ書いて spec に書いていない。**逆向きの網はここだけが持つ** | @@ -524,7 +624,9 @@ Windows の CI でも出ないので、**リークするコードは PR を出 **`add-a-platform` skill を使うこと。** ABI 関数の話ではないが、同じファイル群を 触るのでここから指す。 -直す場所は **少なくとも 17 箇所**ある(**数はあちらが持つ。ここに写さない**)。`.meta` のキー名・ファイル名の衝突・ +直す場所の一覧は `add-a-platform` にある(**数はあちらが持つ。ここに写さない** —— +**以前はここで「少なくとも 17 箇所」と数を書いており、まさに「写さない」と +書いた同じ文が写していた**)。`.meta` のキー名・ファイル名の衝突・ クロスでの `find_package` の閉じ込め・静的ライブラリの依存の束ねなど、 **ビルドが通ってから CI で 8 回落ちた**罠がそこにまとまっている。 diff --git a/.claude/skills/milestone-complete/SKILL.md b/.claude/skills/milestone-complete/SKILL.md index 737261b..7b47c54 100644 --- a/.claude/skills/milestone-complete/SKILL.md +++ b/.claude/skills/milestone-complete/SKILL.md @@ -80,8 +80,25 @@ pwsh tools/dev.ps1 test-tools-slow # CI 専用。ローカルで叩くと数分 pwsh tools/dev.ps1 test-unity-editmode # L4 pwsh tools/dev.ps1 test-unity-player # L5 pwsh tools/dev.ps1 test-unity-tarball # tarball からの導入 +pwsh tools/dev.ps1 test-unity-graphics # M7a。GPU に依る RenderTexture の経路 +pwsh tools/dev.ps1 benchmark # M7a。上の 2 レーンから OCVU_BENCH: を集める ``` +**下の 2 つはどの workflow からも走らない**(`test-unity-tarball` / +`test-unity-web` も同じである)。**CI に配線されていないレーンは、判定のときだけが +唯一の実行機会である** —— 回さなければ誰も回さない。 +`tests/UnityProject/Assets/Tests/EditMode/CiVisibilityTests.cs` が +「CI から見えないテスト」を名指しで固定しているが、それが固定しているのは +まさに `test-unity-graphics` の中身である。 + +**`test-unity-player` は、このマシンでは後始末段階でハングする既知の欠陥を持つ** +(`Stop-UnityTestPlayers` の中の `Get-CimInstance`。roadmap の +`### M7a の判定`)。**テストは完走して結果 XML も書かれるが、レーン全体が無音で +固まる** —— **知らずに踏むと「レーンが赤い」と誤読する。** ハングしたら +プロセスを止め、`tools/assert-unity-results.ps1` を結果 XML に直接掛けて判定する +(M7a が実際にそうした)。**`benchmark` は内部でこのレーンを呼ぶので、 +同じ理由で完走しない。** + **`test-unity-tarball` は既定では 1 platform 分しか確かめない。** 全部入りを検査する には `-PluginSource` で他 platform の plugin 木を渡す必要がある。渡さなければ従来 どおり 1 platform で走って緑になる —— **全部入りのふりはしないが、引数が抜けていた @@ -244,6 +261,14 @@ CI で書けない」である。 黙って消えた指摘は、次に誰かが同じ問題を踏むまで見えない。 +**「次のマイルストーン」が無い場合がある。** `docs/roadmap.md` の `## M` 見出しは +**M7 が最後**で、M8 は無い —— したがって M7 以降の留保には、送り先が構造的に +存在しない。**そのときは roadmap の該当節(または「担当が無かった制約」に相当する +場所)へ、担当つきで落とす。** そして **拾う予定が無いなら、無いと書く** —— +**留保は「誰かがいずれ拾う」と読まれる。** roadmap が「担当が無かった制約」の節を +作ったのは、まさに「本文には書いてあるのに誰の担当でもない」状態が実際に +起きたからである。 + ### 6. AI レビューを済ませてから PR を出す **このリポジトリでは人間のレビューを挟まない。PR を出す前の AI レビューが @@ -333,6 +358,30 @@ GitHub が通す `skipped` / `neutral` は**未達として出る**。それが を書く。**実証済みの条件と同じ見た目にしない** —— 見た目が同じだと、後から読む人は どちらが検査でどちらが宣言かを区別できない。 +### 9. 判定を複数の節に割ったら、残数を書かず、全節の名指し参照を洗う + +**M7 で初めて起きた形である。** 1 つのマイルストーンが 3 つの計画と 1 つの +文書上の決定に割れ、**5 件の完了条件の判定が 4 節に分かれた**(M0〜M6 は +1 節で全条件を判定していた)。そこで 2 つの壊れ方が出た。 + +**(a) 残数を書くと、他の節が 1 件閉じた瞬間にその節だけが古くなる。** +`### M7a の判定` が「残る 3 件」と書き、M7b がそのうち 1 件を消した時点で嘘に +なった。いまは 4 節とも「**ここに残数を書かない**」を明文にしてある。 +**各節は自分が担当する条件だけを判定し、他の条件は担当節を指す。** + +**(b) 節どうしの名指し参照は散文であり、何も検査していない。** +`ci-lint` の documentation link check が見るのは通常の markdown リンク記法だけで、 +**見出しを backtick で引用しただけの参照(`` `### M7c の判定` `` のような形)は +対象外である。** 実際、その警告を書いた branch の中でさらに 1 件を**2 人がかりで +見落とした。** **条件を 1 つ閉じたら、既存の判定節にある名指しの参照を自分で +洗って直すこと** —— 見落としても機械は何も言わない。 + +**そして、全体の判定がどこにも無い状態になりうる。** M7 は 5 件とも「満たした」 +なのに、**「M7 は完了した」と書いた文が 1 つも存在しない**まま終わった +(2026-09-10 に索引としての `### M7 の判定` を足して閉じた)。 +**割ったなら、索引になる節を 1 つ置くこと。** そこには判定の本文を写さず、 +**どの節が何を判定したか**と、**満たしたが実証していないもの**を並べる。 + ## やってはいけないこと - テストが緑であることを完了条件の代わりにする diff --git a/.claude/skills/prove-a-check-works/SKILL.md b/.claude/skills/prove-a-check-works/SKILL.md index 82b002e..e66194c 100644 --- a/.claude/skills/prove-a-check-works/SKILL.md +++ b/.claude/skills/prove-a-check-works/SKILL.md @@ -49,6 +49,25 @@ cmake の stdout を渡していた。内容は同じでも行頭が違い、本 T7 も同じで、計画が推測した期待文字列(`FFMPEG: YES` の桁揃え)は実物に一度も現れず、 書いていれば永久に通る assertion になっていた。 +**入力の「形」だけでなく、検査が当たっている「対象そのもの」も確かめる。** +M7 で 2 件出た。**どちらも検査は緑で、当たっていたのが本番の物ではなかった。** + +- **開発用の成果物を、配布物の代理にしていた。** `tools/verify-exported-symbols.ps1` + の docstring は「**配布 binary の公開面**」を検査すると書いていたが、配線されて + いたのは `ci-native.yml` だけで、当たっていたのは **`dev.ps1 test` が作る開発用の + binary** だった。**配る経路(`release.yml`)には 1 度も掛かっていない。** + M3 で `verify-plugin-portability.ps1` が踏んだ形と 1 対 1 で、`release.yml` にも + 配線して閉じた。 +- **手書きの probe を、生成物の代理にしていた。** profile の gating を Unity に問う + 正の対照は**手書きのコード**で満たされており、**生成物を 1 度もコンパイルして + いなかった。** その穴を通って `[DllImport(LibraryName, ...)]` が `CS0103` になる + 欠陥が入った —— 他の検査(出力の**文字列**を見るもの、`--list-outputs` が返す + **パス**を見るもの)はどれも捕まえなかった。 + +**合言葉: 開発用の成果物・手書きの probe・合成した入力は、本番の代理ではない。** +docstring が「配布物を見る」と書いているなら、**配る job の中で走っていることを +`grep` で確かめる。** + ### 3. 壊して、落ちることを見る **これを飛ばしてよい検査は無い。** @@ -266,6 +285,29 @@ M4 の 1 ブランチだけで 4 通り出た。**どれも「通るのに何も 自分が使う実装の**アンカーの定義**を読み、境界(末尾の改行、`\r\n`、中間の改行)を 1 つずつケースにすること。 +### 門を何本置くか —— 単独で何を捕まえるかで数える + +**本数で決めない。1 本ずつ「これだけで何が捕まるか」を言えるかで決める。** + +**足りない側**(2026-09-07、`tools/tests/OpenCvConfig.Tests.ps1`)。`dev.ps1` の +Unity レーンが `-testCategory` の除外を `$script:UnityGraphicsExclusionCategory` +から取っていること、を守る 2 本:「**正しい形が在る**」(引数の形を含む)と +「**間違った形が無い**」(否定形の `-testCategory` リテラルが 1 つも無い)。 +**どちらも単独では素通りする** —— 前者は直書きへ戻したうえでコメントに引数の形を +書けば PASS し(正規表現はコードとコメントを区別しない)、後者はシングルクォート +だけを見ていた版が `"-testCategory", "!Graphics"` を通した(どちらも実測)。 +**向きが違うので、片方を消すとその向きが丸ごと開く。** だからコード側に +「対になって初めて 1 つの門になる」と書いてある —— **書いておかないと、後から +読む人が片方を冗長として消す。** ただし**塞ぎきったようにも読ませない**: +splatting や文字列連結で組み立てた形はどちらも素通りする、と同じコメントが自認している。 + +**多すぎる側**は上の「数を見る検査は、誤った読みに満たされる」にある —— +**同じ向きを別の角度から見ているだけの門は、束にしても釣り合わせられる。** +構造の門 1 本がそれらを吸収するなら、**消すのが正しい。** + +**見分け方はひとつ**: その 1 本を消したとき、**他のどれも捕まえない壊し方**を +挙げられるか。挙げられれば対であり、挙げられなければ面積である。 + ### 検査が別の検査を壊していないか(並行実行) M4 で、自分が書いたテストが**固定名の一時ファイル**を使い、`dev.ps1 test` が @@ -583,6 +625,15 @@ cmd | tail -3; echo $? # これは tail の終了コード 気づいたのは、無関係な作業の途中で 1 度手で回したからである。**それが無ければ 次のリリースまで誰も知らなかった。** +**同じレーンが、同じ理由で、M7a でまた壊れた。** `Test-UnityTarball` だけが +`-testCategory '!Graphics'` を渡しておらず、`-nographics` の下で +`[Category("Graphics")]` の 6 件が skip されずに fail した(実測: 除外前は +66 件中 6 件失敗・exit 1、除外後は 59 件全通過・exit 0)。**直るまで CI は +緑のままだった** —— このレーンはどの workflow からも走らないからである。 +**教訓は「一度直したから大丈夫」ではない: 走らないレーンは、直した後も +同じ速さで腐る。** なお `unity-probe.yml` にも同じ抜けが**分かっていて +残してある**(意図的な例外は、意図的だと書いてあれば腐らない)。 + **レーン単位の配線確認では足りない —— 入口が複数あるときは。** 2026-09 の API 拡張で実測した。1 つの検証本体 (`tests/UnityProject/Assets/Tests/Shared/AbiSurfaceChecks.cs`)に 3 つの入口がある: @@ -642,7 +693,7 @@ M5 では `CvQrCode.Encode` が `dst` を丸ごと差し替えることが公開 同じリポジトリの `CvCodecs.Decode` は明記していた。**書き分けが不統一なら、 どちらかが嘘である。** -### 合計だけを固定した検査は、中身の入れ替えを通す +### 数を見る検査は、誤った読みに満たされる 境界に出す struct の layout を `sizeof == 28` と `Marshal.SizeOf == 28` の両側で 固定しても、**同じ型のフィールドを入れ替えると両方通る**(`float x, y` を @@ -651,6 +702,39 @@ M5 では `CvQrCode.Encode` が `dst` を丸ごと差し替えることが公開 **`Marshal.OffsetOf` を全フィールド分並べる**と閉じる。壊れ方は「呼んだ場所ではなく、 後から値を読む無関係な場所」型なので、合計だけでは足りない。 +**2026-09-10 に、同じ形を 3 回作った。** 一覧を写すのをやめて正本 +(csproj)から読むようにした hook で、**「ちゃんと読めたか」を数で守ろうとした**: + +| 版 | 門 | 何がすり抜けたか | +| --- | --- | --- | +| 1 | フォルダが **2 つ以上**読めた | 4 つのうち **2 つしか読めない**誤読が、2 つ読めているという理由で通った。**2 フォルダが黙って無防備になった** | +| 2 | **合計 3 本**(要素数の下限・フォルダ数 = パス数・フォルダ数 ≧ 要素数) | **同時に満たせた。** 3 パスを持つ要素 1 つと、1 つも読めない要素 1 つで**要素 2 / パス 3 / フォルダ 3** —— 3 本とも成立し、1 フォルダが無防備になった | +| 3 | **要素ごとに**、フォルダがちょうどそのパス数だけ取れたか | —— | + +**版 2 は版 1 の「難しくした版」でしかない。** 数を 1 つから 3 つに増やしても、 +**それらは全部が合計に対する算術**なので、多く読めた側と読めなかった側で +釣り合ってしまう。**閾値を上げても、問いは変わっていない。** + +**閉じたのは、問いを変えたときである** —— 「合計は十分に大きいか」から +**「フォルダを出すはずだった要素は、全部フォルダを出したか」**へ。 +**合計は集約であり、情報は要素の側にある。** + +**そして構造の規則は、数の門を消せる。** 版 3 の 1 本が版 2 の 3 本のうち +**2 本を吸収した**(「パスを 1 つ落とした」も「要素ごと落とした」も、要素の +中で見れば同じ 1 つの不一致になる)。残ったのは下限 1 本だけで、そちらは +要素ごとの検査では原理的に代替できない —— **要素が 0 個なら、要素ごとの検査は +空虚に真になる。** **重なる門を 3 つ置くのは、安全ではなく面積である** +(対にする価値がある場合との違いは、下の「門を何本置くか」)。 + +**一般形: 検査が何かを数えているなら、その数が正しく見えるかではなく、 +「誤った読みは何を出すか」を問うこと。** 数は代理であって、欲しい性質そのもの +ではない。**欲しい性質を言葉にして、それを検査する。** + +**3 版とも、見つけ方は同じだった** —— 読み返してではなく、 +**著者が想像していなかった形を実際に食わせて**である(この skill の主題そのもので、 +これはその強い実例である)。版 2 を破ったのは、こちらが 1 度も書いたことのない +`Include =` という空白の入れ方だった。 + ### ディレクトリが在ることは、中身が在ることではない M5 の作業中、`test-tools-slow` を中断した結果 **OpenCV のツリーが空になった**。 @@ -728,6 +812,26 @@ M5 の `calib` で、実装者と再レビュアーが同時に同じソース **コンパイルが通らない**)。あちらは**より強い**形で、こちらは**弱い**形である —— どちらも「同じ言葉で記録してはいけない」という点では同じである。 +**3 例目(M7c、2026-09-10)。これまでで最も弱い。** `native/src/ocvu_dnn.cpp` の +`.clone()` について、実物の ONNX で 2 回 forward する負の対照を試みたが、 +**外しても検知できなかった。** それだけなら上のステレオと同じだが、こちらは +**なぜそのコードが要るのかを確定させる経路がリポジトリの構成上存在しない** —— +当初の説明(`cv::Mat` の参照カウント)はレビューで否定され、いまの主説 +(新しい engine が毎回新しいバッファを返す)は未確認のままで、 +**`./tools/opencv.ps1 restore` が復元する木はヘッダと lib だけで +`cv::dnn::Net::forward()` の実装ソースを含まない。** + +**この 2 つは別の主張である。区別して書くこと。** + +| 書けないこと | 意味 | +| --- | --- | +| 「壊したら落ちた」 | **検査が働くことを示せていない** | +| 「このコードが要る」 | **そのコードの必要性を示せていない** | + +ステレオ(2026-09-05)は前者だけだったが、`.clone()` は**両方**である。 +コメントには「**契約を成立させている唯一のものとして残してあるが、その必要性を +示す再現テストはまだ無い**」と書き、**安全装置として残す判断そのものを記録した。** + ## やってはいけないこと - 通ったことを、検査していることの証拠として報告する diff --git a/CLAUDE.md b/CLAUDE.md index 72f90ec..708c57c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -127,7 +127,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | `tools/opencv.ps1` | OpenCV の `restore` / `build` / `verify` / `status` / `clean` の入口 | | `tools/verify-opencv-artifact.ps1` | ビルド済み OpenCV ツリーに対する依存 allowlist の検証(denylist ではない) | | `tools/verify-artifact-linkage.ps1` | 成果物の linkage が構成の意図と一致するかの検証(M3 Task 3/5。送った CMake flag ではなく `.lib`/`.a` を読む)。Windows は `DEFAULTLIB`、macOS/Linux は `nm -u`(リンク済み依存)・`lipo`/`readelf`(linkage)・`ar t` のメンバ名(**有効言語**——`foo.S.o` が出たらアセンブラが有効化された)で判定。**3 platform とも実物の artifact に対して CI で green。** 負の経路(アセンブル済み object を詰めた合成 archive)も macOS/Linux の CI で落ちることを確認済み。このマシンには `nm`/`ar` が無いのでローカルでは SKIP と出る——SKIP は「確かめていない」であって「合格」ではない | -| `tools/verify-exported-symbols.ps1` | **binary の公開面が spec と完全一致することの検証(M7b)。** 走るのは `ci-native.yml`(`dev.ps1 test` が作った開発用の binary)と `release.yml`(**配る package に置かれた実物**)の 2 経路で、後者は最終レビューで足した —— それまで「配布 binary の公開面」と書いてありながら、配る経路には 1 度も掛かっていなかった(`verify-plugin-portability.ps1` が M3 で踏んだ形と同じ)。**どちらの経路も desktop 3 platform だけである** —— iOS / Web は静的ライブラリで「export 面」の概念が違い(`verify-static-bundle.ps1` が別に見る)、Android は共有ライブラリだが host の `nm` が arm64 の ELF を読めるかを確かめていないので明示的に skip する。 Windows は `dumpbin /EXPORTS`、Linux は `nm -D`(動的シンボル表)、macOS は `nm -g`(Mach-O には `.dynsym` に相当する別表が無いのでこちらで足りる)で実物の export を読み、`ocvu_` で絞ったうえで spec の関数名(`entryPoint` を持つ entry は畳む)と過不足なく突き合わせる。**両向きに効く** —— spec にあるのに export されていない関数も、export されているのに spec に無い関数も検出する。**`ocvu_` で絞る理由は実測で裏づいている**——3 platform とも `ocvu_` の export 53 本は spec の 53 本と一致した一方、絞りを外すと Linux で 3 件(`cv::cornerScore` のテンプレート実体化)、macOS で 1895 件の非 `ocvu_` export が出た(**macOS の 1895 は `nm -g` という道具の性質かもしれない** —— 実際の動的 export 面がもっと小さい可能性は測っていない)。`native/modules.cmake` の `OCVU_MODULES` を絞ると、外した module の関数が実際にこの検査で「不足」として捕まる | +| `tools/verify-exported-symbols.ps1` | **binary の公開面が spec と完全一致することの検証(M7b)。** 走るのは `ci-native.yml`(`dev.ps1 test` が作った開発用の binary)と `release.yml`(**配る package に置かれた実物**)の 2 経路で、後者は最終レビューで足した —— それまで「配布 binary の公開面」と書いてありながら、配る経路には 1 度も掛かっていなかった(`verify-plugin-portability.ps1` が M3 で踏んだ形と同じ)。**どちらの経路も desktop 3 platform だけである** —— iOS / Web は静的ライブラリで「export 面」の概念が違い(`verify-static-bundle.ps1` が別に見る)、Android は共有ライブラリだが host の `nm` が arm64 の ELF を読めるかを確かめていないので明示的に skip する。 Windows は `dumpbin /EXPORTS`、Linux は `nm -D`(動的シンボル表)、macOS は `nm -g`(Mach-O には `.dynsym` に相当する別表が無いのでこちらで足りる)で実物の export を読み、`ocvu_` で絞ったうえで spec の関数名(`entryPoint` を持つ entry は畳む)と過不足なく突き合わせる。**両向きに効く** —— spec にあるのに export されていない関数も、export されているのに spec に無い関数も検出する。**`ocvu_` で絞る理由は実測で裏づいている**——3 platform とも `ocvu_` の export が spec の関数一覧と過不足なく一致した一方(**本数を写さない。正本は `docs/api-map.md` の冒頭である** —— 写していた「53 本」は M7c の `dnn` が入った時点で古くなった)、絞りを外すと Linux で 3 件(`cv::cornerScore` のテンプレート実体化)、macOS で 1895 件の非 `ocvu_` export が出た(**macOS の 1895 は `nm -g` という道具の性質かもしれない** —— 実際の動的 export 面がもっと小さい可能性は測っていない)。`native/modules.cmake` の `OCVU_MODULES` を絞ると、外した module の関数が実際にこの検査で「不足」として捕まる | | `tools/verify-plugin-portability.ps1` | Linux plugin が要求する GLIBC / GLIBCXX の上限検査。**readelf に頼らず ELF を直接読む**ので Windows の開発機でも動く(道具が無いから検査できない、という穴を作らない)。上限は「支える最も古い環境」= Ubuntu 22.04(glibc 2.35)| | `tools/ci/setup-linux-container.sh` | Linux のビルドコンテナに道具を入れる。cmake は Kitware から(jammy の apt は 3.22.1 で古すぎる)。git の safe.directory も設定する(コンテナは root、ファイルは runner の UID)| | `tools/tests/OpenCvConfig.Tests.ps1` | 構成ハッシュの検査(`dev.ps1 test` に入る速いレーン)に加えて、**workflow 側の不変条件を job 単位で**見る: `container:` を持つ job の宣言イメージが `opencv-config.psd1` と一致すること、その job の `run:` に `sudo` が残っていないこと、`opencv.ps1 restore` を呼ぶ job が `third_party/opencv/` を対象にした cache step を**ちょうど 1 つ**持ち、その `path:` と `key:` の両方が構成ハッシュを参照し、参照先の step id が同じ job に実在すること、テストレーンを走らせる job が結果を `if: always()` で artifact 化すること、全 job に `timeout-minutes` があること、配る binary を作る job が step 単位の `if:` 無しで移植性を検査し、その振り分けが matrix の platform 集合と過不足なく一致すること。**Unity を起動する job については**(`ci-unity` の中で `game-ci/unity-test-runner` を使う job だけに絞る。絞った先が空でないことも見る)、**全 platform を重ねる step**・合図を置く step・`assert-unity-results.ps1` に `-RequireTest` を渡す step がそれぞれちょうど 1 つあること、`needs:` を持つなら**ちょうど `if: !cancelled()`** で守られていること(ゆるい条件は必須チェックを**常時** skip にしうる)、合図の名前が C# の定数・workflow・`dev.ps1`・`.gitignore` の 4 箇所で一致していること。**job 単位にしたのは、ファイル単位では通ってしまう穴を実際に踏んだから**である——cache の検査が `ci-native` の Windows job の分で満足し、後から足した macOS / Linux と `ci-sanitizers` の linux-asan が漏れていた。**コンテナ名の検査も同じ穴を持っていた**: 4 本の workflow を名指ししていたので `nightly` を見落としており、`container:` を持つ全 job を対象にする形に直した。切り出しは YAML パーサを使わずインデント規約で行うが、**切れなかったときは空振りではなく落ちる**(`jobs:` 直下の 2 スペース key を緩く数えた数と、job として認識した数が合わなければ失敗させる)。**ファイル単位で見るもの**は 3 つある: `# shellcheck ` で始まる散文(道具が指示文として読んでしまう)、`tools/tests/*.Tests.ps1` が `dev.ps1` の `$ToolsTestScriptsFast` / `$ToolsTestScriptsSlow` に配線されていること、`.github/codeql/codeql-config.yml` の `query-filters`(下の行)。workflow の列挙は `.yml` と `.yaml` の両方を拾い、`git ls-files` の一覧と突き合わせる——**検査対象から静かに外れる**が構造上ありえないようにするため。**リポジトリ全体で突き合わせるもの**が 2 つある: 全 workflow の `uses:` が固定された版であること(`@main` / `:latest` を禁じる)と、**同じ action が 2 つの版で参照されていないこと**(名前は `owner/repo` に丸めるので、`codeql-action/init` だけ上げて `analyze` を忘れると落ちる)、そして全 `.csproj` の `PackageReference` が同じ版であること。**後ろ 2 つはどちらも実際に踏んだ形である** —— `actions/upload-artifact` が `unity-probe.yml` でだけ v6 のまま残り、**どの workflow も緑のまま**だった(2026-09-01 に発見。unity-probe は手動起動なので、次に誰かが起動するまで走りもしない)。拾えた `uses:` の数が緩い数え方と合わなければ、空振りではなく落とす | @@ -180,10 +180,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | `ci-native.yml` | push(main) / PR / 手動 | `dev.ps1 test` を 3 platform で(**L0 + L1 + L3**。M5 以降、生成物と spec の一致検査もここに乗る)。**desktop 3 job とも `test-tools-slow` を配線してある。**クロスの 3 job(Android / iOS / **Web**)はビルドのみ | **3 platform とも green**(M3 の PR #8 以降) | | `ci-sanitizers.yml` | push(main) / PR / 手動 | L2。Windows は ASan、Linux は ASan+LSan(リーク検出はこのレーンだけが担う) | green | | `build-opencv.yml` | 手動 + 構成ファイルの変更 | allowlist 構成の OpenCV を**全 platform**でビルドし artifact 公開(**数を写さない。正本は `tools/opencv-config.psd1` の `Toolchains`**) | 稼働中 | -| `ci-unity.yml` | push(main) / PR / 手動 | L4(EditMode)+ L5(IL2CPP Player)。**Unity は ubuntu で走る**が、**2026-08-30 から他 platform の plugin もビルドし、Unity job がそれを重ねてから走る**(**現在は** `windows-2022` / `macos-14` を含む plugin の 5 job で、全部入りの gating を**要素 6 個**の集合に当てる。**M4 の直後は 4 job / 要素 5 個で、M6 で 1 つずつ増えた**)。**M6 で 3 つ目の job `Web browser E2E` が加わった** —— WebGL の Player を建て、**Linux の headless Chromium で実際に走らせる**(Unity Test Framework は WebGL の Player を batchmode から走らせられないので、Player 自身に検証本体を走らせて結果をブラウザのコンソールへ出し、それを外から読む)(Windows で走らせない理由として記録していたものは 2026-08-29 に崩れた。上記参照)。したがって CI の L5 は Linux の IL2CPP Player で、Windows 版はローカルのレーンが担う。Unity の導入とアクティベーションは game-ci、合否の判定は `tools/assert-unity-results.ps1`(ローカルと共通)。Unity の版は `ProjectVersion.txt` から取るので、載せ替えても workflow は変えなくてよい。**`game-ci/unity-test-runner` は `v5.0.0-beta.1` の commit を指している**(2026-09-01。タグではなく SHA —— pre-release のタグは上流が消せる)—— 安定版はどれも `node20` を宣言しており、**この repo で node20 を宣言する action はこれだけだった**。`action.yml` の差は `node20` -> `node24` の 1 行だけで、入力 24 個・出力 2 個は名前も既定値も同一である(実測)。**ただしそれはインタフェースの話で、挙動の証明ではない** —— 中身の `dist/index.js` は実装が game-ci/cli へ移ったことで大きく入れ替わっている。**pre-release を必須チェックに入れている**ことは自覚しておくこと(上流は「main CI ではなく別 workflow で試すのが安全」と書いている)。安定版が出たら差し替える。**beta に切り替えたのは 2026-09-01(PR #57)で、それ以降の green は beta のものである** —— M6 の PR #63(2026-09-03)と、この API 拡張の PR #69(2026-09-05)がそれに当たる。**それより前の green(M3.5 / M4 / M5 の 4 件)は `@v4` のものである** | **green**(2026-08-29 に初めて。main の先端 `68fbdae` でも green)。**ただしM3.5 で Unity を 6000.3.16f1 へ、EditMode を 16 件へ替えた後も green**(run の詳細は `docs/roadmap.md` の M3.5 節。**同じ事実を 2 箇所に書かない**)。**2026-08-30 に 3 platform 構成でも green**(PR #37、run 33290375806。`native plugins present: 3` と gating 4 件の個別 Passed を CI が出した)。**M4 で 5 platform 構成になった後も green**(PR #41。`native plugins present: 5`、EditMode 33 passed / Standalone 18 passed)。**M5 で到達性テストが 1 件加わった後も green**(PR #55、2026-09-01。`==> [EditMode] 34 passed` / `==> [Standalone] 19 passed` で**ローカルの実測と一致**。Standalone は stripping 済みの実物で、生成した P/Invoke が全部解決した)。**M6 で Web が加わった後も green**(PR #63、2026-09-03。`native plugins present: 6` と gating 4 件、そして新しい `Web browser E2E` が `passed=8 failed=0 reachable=28`。**この時点では EditMode / Standalone の件数は M5 から変わっていなかった** —— M6 は platform を足したのであって検証本体を足したのではない)。**2026-09 の API 拡張で 26 本を足した後も green**(PR #69、2026-09-05、run 33945388389。`==> [EditMode] 48 passed` / `==> [Standalone] 33 passed` / `native plugins present: 6` / `Web browser E2E` が `passed=21 failed=0 reachable=54`。**3 レーンとも件数が動いた最初の変更である** —— 公開 API を Unity の中で動かす検査を共有本体に 13 件足したので、**手書きで配線する EditMode / PlayMode と、リフレクションで拾う Web が同時に増えた**)| +| `ci-unity.yml` | push(main) / PR / 手動 | L4(EditMode)+ L5(IL2CPP Player)。**Unity は ubuntu で走る**が、**2026-08-30 から他 platform の plugin もビルドし、Unity job がそれを重ねてから走る**(**現在は** `windows-2022` / `macos-14` を含む plugin の 5 job で、全部入りの gating を**要素 6 個**の集合に当てる。**M4 の直後は 4 job / 要素 5 個で、M6 で 1 つずつ増えた**)。**M6 で 3 つ目の job `Web browser E2E` が加わった** —— WebGL の Player を建て、**Linux の headless Chromium で実際に走らせる**(Unity Test Framework は WebGL の Player を batchmode から走らせられないので、Player 自身に検証本体を走らせて結果をブラウザのコンソールへ出し、それを外から読む)(Windows で走らせない理由として記録していたものは 2026-08-29 に崩れた。上記参照)。したがって CI の L5 は Linux の IL2CPP Player で、Windows 版はローカルのレーンが担う。Unity の導入とアクティベーションは game-ci、合否の判定は `tools/assert-unity-results.ps1`(ローカルと共通)。Unity の版は `ProjectVersion.txt` から取るので、載せ替えても workflow は変えなくてよい。**`game-ci/unity-test-runner` は `v5.0.0-beta.1` の commit を指している**(2026-09-01。タグではなく SHA —— pre-release のタグは上流が消せる)—— 安定版はどれも `node20` を宣言しており、**この repo で node20 を宣言する action はこれだけだった**。`action.yml` の差は `node20` -> `node24` の 1 行だけで、入力 24 個・出力 2 個は名前も既定値も同一である(実測)。**ただしそれはインタフェースの話で、挙動の証明ではない** —— 中身の `dist/index.js` は実装が game-ci/cli へ移ったことで大きく入れ替わっている。**pre-release を必須チェックに入れている**ことは自覚しておくこと(上流は「main CI ではなく別 workflow で試すのが安全」と書いている)。安定版が出たら差し替える。**beta に切り替えたのは 2026-09-01(PR #57)で、それ以降の green は beta のものである** —— M6 の PR #63(2026-09-03)、API 拡張の PR #69(2026-09-05)、そして M7 の PR #70 / #72 / #73(2026-09-06〜10)がそれに当たる。**この列挙は網羅を主張しているので、PR を 1 本通すたびに伸ばすか、やめるかのどちらかである** ——「#69 で終わっている」状態が実際に起きた。**それより前の green(M3.5 / M4 / M5 の 4 件)は `@v4` のものである** | **green**(2026-08-29 に初めて。main の先端 `68fbdae` でも green)。**ただしM3.5 で Unity を 6000.3.16f1 へ、EditMode を 16 件へ替えた後も green**(run の詳細は `docs/roadmap.md` の M3.5 節。**同じ事実を 2 箇所に書かない**)。**2026-08-30 に 3 platform 構成でも green**(PR #37、run 33290375806。`native plugins present: 3` と gating 4 件の個別 Passed を CI が出した)。**M4 で 5 platform 構成になった後も green**(PR #41。`native plugins present: 5`、EditMode 33 passed / Standalone 18 passed)。**M5 で到達性テストが 1 件加わった後も green**(PR #55、2026-09-01。`==> [EditMode] 34 passed` / `==> [Standalone] 19 passed` で**ローカルの実測と一致**。Standalone は stripping 済みの実物で、生成した P/Invoke が全部解決した)。**M6 で Web が加わった後も green**(PR #63、2026-09-03。`native plugins present: 6` と gating 4 件、そして新しい `Web browser E2E` が `passed=8 failed=0 reachable=28`。**この時点では EditMode / Standalone の件数は M5 から変わっていなかった** —— M6 は platform を足したのであって検証本体を足したのではない)。**2026-09 の API 拡張で 26 本を足した後も green**(PR #69、2026-09-05、run 33945388389。`==> [EditMode] 48 passed` / `==> [Standalone] 33 passed` / `native plugins present: 6` / `Web browser E2E` が `passed=21 failed=0 reachable=54`。**3 レーンとも件数が動いた最初の変更である** —— 公開 API を Unity の中で動かす検査を共有本体に 13 件足したので、**手書きで配線する EditMode / PlayMode と、リフレクションで拾う Web が同時に増えた**)| | `ci-lint.yml` | push(main) / PR / 手動 | 4 job: actionlint(workflow の構文・式・`run:` の中の shell。**版は `1.7.12` に固定してある** —— `:latest` だと上流が規則を足した日に、差分が無いまま必須チェックが赤くなる)/ shellcheck(hook と CI のスクリプト)/ PSScriptAnalyzer(`tools/` の PowerShell、Error のみ)/ 文書の相対リンク検査(コードブロックの中は見ない。0 件なら「壊れていない」ではなく走査が効いていないとして落とす)。**静的に読めば分かる誤りを CI 1 周(10〜20 分)かけて確かめていた**のを埋める。数分で終わるので重いレーンより先に落ちる | green | | `codeql.yml` | push(main) / PR / 週 1 / 手動 | C++ と C#。C++ は**配布する `opencv_unity_native` だけ**をビルドする——`paths-ignore` は C++ に効かず(解析対象は「実際にコンパイルされたもの」)、テストごとビルドすると FetchContent の GoogleTest の指摘が混ざるため。C# は P/Invoke していること自体への 2 規則を `query-filters` で外す(**設計どおりの指摘に本物が埋もれる**ため。実測で open 107 件中 84 件がこれで、その陰に本物が 4 件あった)。OpenCV の cache は `if: matrix.language == 'c-cpp'` で c-cpp の leg に限る——csharp の leg は OpenCV を一度も開かないので、揃えないと cache hit 時に使わない木を落として展開し、miss 時は post step が `Path Validation Error` を出して何も保存しない(後者は「なぜ cache が温まらないのか」を調べる人に偽の手がかりを渡す) | green。埋もれていた本物 4 件の現状は下記 | -| `nightly.yml` | 毎日 04:00 UTC / 手動 | 誰も push していない間に壊れることを見つける。**3 job 定義**(速いレーンの job は `lanes` という 2 runner の matrix なので、**実行時は 4 件**になる): Linux 成果物の移植性(`ubuntu:22.04` でビルドし直して GLIBC の要求を見る)/ Windows・macOS の速いレーン(`dev.ps1 test`。結果を `if: always()` で artifact 化する。名前は `matrix.runner` で分ける——`matrix.name` は空白を含み、分けないと 2 つの job が同じ artifact 名を取り合う)/ OpenCV artifact の期限切れ確認 | **schedule で起動した実績はまだ無い。** 手動起動が 2 回あり、1 回目(run 33230097557、2026-08-29 02:54Z)は 4 job 中 3 job が API レート制限で失敗、2 回目(run 33233610215、同 04:21Z、修正後)は 4 job とも success | +| `nightly.yml` | 毎日 04:00 UTC / 手動 | 誰も push していない間に壊れることを見つける。**3 job 定義**(速いレーンの job は `lanes` という 2 runner の matrix なので、**実行時は 4 件**になる): Linux 成果物の移植性(`ubuntu:22.04` でビルドし直して GLIBC の要求を見る)/ Windows・macOS の速いレーン(`dev.ps1 test`。結果を `if: always()` で artifact 化する。名前は `matrix.runner` で分ける——`matrix.name` は空白を含み、分けないと 2 つの job が同じ artifact 名を取り合う)/ OpenCV artifact の期限切れ確認 | **2026-08-29 から schedule で毎日走っている。** 内訳(実行回数・成否)は `docs/roadmap.md` のワークフローの節が持つ —— **ここに写さない**(毎日増える数である)。**「schedule でまだ 1 度も走っていない」と書いてあったのは逆向きの陳腐化で、毎日動いている検査を存在しないことにしていた** —— 穴が埋まっても誰も赤くならないので、この向きの誤りは自然には見つからない | | `unity-probe.yml` | **手動のみ** | **Linux 以外で Unity が動くかの探査**(M4 の条件 6・7)。恒久レーンではない —— **結論を出すための道具**である。5 回分の壁が step のコメントに書いてあり、`game-ci は macOS を支えない` / `Windows のコンテナに MSVC が無い` / `Unity Hub は --headless でも architecture を聞く` / `macOS では entitlement が 0 件` を**同じ疑問を持った次の人が調べ直さずに済む**形で残してある | **5 回実行**(run 33350726005 / 33352025223 / 33356182306 / 33358384921 / 33361012965)。結論は roadmap の「担当が無かった制約」にある | | `release.yml` | tag `v*` / **PR** / 手動 | **6 platform 分**の UPM tarball と配布物 4 点、**配る正である全部入りの tarball `com.ayutaz.opencv-unity-native.tgz` とその `checksums.txt`**、`SHA256SUMS.txt` を GitHub Release へ(staging する asset は **32 件** = 6 × 5 + 全部入りの 2 件、`SHA256SUMS.txt` を足して Release には **33 件**)。**数だけでは足りない**ので、全部入りが実際に並んでいることも名指しで見る。全部入りに **SBOM と build-manifest は付けない**(理由は上の `tools/package-release.ps1` の行)。M3.5 で各 matrix job が `Runtime/Plugins/` も artifact 化するようになり、**`assemble` job が重ねて固める**(`publish` job は出来た asset を受け取って Release にするだけ)。asset にするのは `upm-out/` と `release-out/` の中身だけで、**生の binary は asset にしない**。`--draft` で下書きを作り、点検してから人が公開する(**tag を打っただけでは外から見えない**)。ノートは `.github/release-notes.md` から読む(YAML の中の PowerShell の中の Markdown という三重のエスケープを避けるため) | **M4 以降は PR でも空撃ちする。** それまで配る経路は tag と手動でしか走らず、**M4 で欠陥が 3 件たまった** —— Android の `sdk/etc/licenses/` を知らない(tag を打つと Release が 1 件も作られない)/packer の中の 2 つ目の一覧が 3 platform のまま/`package-release.ps1` が binary を拡張子で拾い iOS の `.a` を落とす。**3 件とも CI は緑のままで、2 件は空撃ちを 1 回回すまで誰も知らなかった。** 費用は安い(実測: 5 platform で合計 5 分弱、並列 2 分)ので `paths` で絞らない —— **絞ると必須チェックにできない**(当たらない PR では check が現れず、必須にすると永久に merge できない)。**v0.1.0 / v0.1.1 で実行済み**(どちらも M3.5 より前の形)。**M3.5 が足した全部入りの配線は 2026-08-30 の空撃ちで初めて通した**(run の詳細は `docs/roadmap.md` の該当行。**いまの 2 job 構成での実績は run 33289128197**)。**そのために job を `assemble` と `publish` に割った** —— 以前は組み立ても公開も 1 つの job にあり、その job ごと tag に限っていたので、**束ねる側は tag を打つまで 1 行も動かなかった** | @@ -218,6 +218,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `docs/superpowers/plans/2026-08-26-m2-windows-vertical-slice.md` — M2 の実装計画(タスク単位、TDD 手順つき) - `docs/superpowers/plans/2026-08-28-m3-desktop-three-platforms.md` — M3 の実装計画(タスク単位、TDD 手順つき) - `docs/superpowers/plans/2026-08-30-m3.5-distribution-shape.md` — M3.5 の実装計画(タスク単位、TDD 手順つき) +- **M4 以降の計画をここに並べない。** M4 / M5(本体 1 本と続きの 3 本)/ M6 / M7(4 本)/ 2026-09 の API 拡張(6 本)まで含めた一覧は `docs/README.md` の「実装計画」が持ち、状態注記もそちらにある(**この 5 行を伸ばすと 2 箇所が同時に古くなる。実際この一覧は M3.5 で止まったまま 4 マイルストーン分置いていかれた**)。着手時に開く計画は上の「実装に着手するとき」2 が名指しする - `docs/openupm-registration.md` — OpenUPM へ出した登録定義と手順。**2026-08-30 に提出し、受理された**(openupm/openupm PR #6843) - `docs/abi-ownership-and-versioning.md` — `Mat` と Unity メモリの所有権契約、`OCVU_ABI_VERSION` の versioning 規約、**リンクしている 9 module の** API allowlist の正本 - `docs/unity-opencv-integration-research-and-plan.md` — 競合調査、アーキテクチャ、ライセンス方針、命名方針 @@ -324,7 +325,7 @@ C++ を選んだ主因は、**sanitizer が安定版ツールチェーンで使 再評価を安価に保つため、**public C header と契約テスト(L1 / L3)は backend 実装から独立に保つ**。この不変条件は M0 で確立し、以降のすべてのマイルストーンで維持する。 -## マイルストーン(現在地: **M6(Web / Wasm)は完了条件 5 件すべてを満たした**(2026-09-03、PR #63 → `1e68d27`)—— **Web が 6 つ目の platform になった**(判定表は `docs/roadmap.md` の M6 節)。**M6 が足した 3 本の check を必須にするかは下の「機構として強制されていること」が持つ**(**ここには書かない** —— 昇格した日に、写した側だけが嘘になる)。**Web にだけ在る制限が 1 つある: `imgcodecs` は JPEG のみで PNG を持たない**(Unity の WebGL 支援が自前の libpng を同梱しているため。他の 5 platform は両方持つ)。その前の **M5(binding specification と generator)も完了条件 5 件すべてを満たした**(2026-09-02)。生成の仕組みは `docs/superpowers/plans/2026-08-31-m5-binding-generator.md` で成立し、**条件 2(`geometry` / `calib` / `features` / `objdetect` の追加)は 3 つの続きの計画で閉じた** —— `objdetect` / `features`(`.superpowers/sdd/2026-09-01-m5-modules-objdetect-features/`)、`geometry`(同上の続き。**リンクは無料だった** —— `features` / `objdetect` の依存として推移的に引かれており `COMPONENTS` に足す必要すら無かった)、カメラの歪み補正(`docs/superpowers/plans/2026-09-01-m5-calib-undistort.md`)、そして **`calib` module と `cv::calibrateCamera`**(`docs/superpowers/plans/2026-09-02-m5-calib-camera.md`)。**最後の 1 つだけが高かった** —— 構成ハッシュが `4785d98e9aad` → `09fcbe260d87` に変わり、5 platform 分の OpenCV を作り直した。**本数は `docs/api-map.md` の冒頭が数える。****M4(Mobile)も完了していない** —— 完了条件 9 件のうち **3 件**が閉じていない(iOS 実機 / lifecycle / macOS 上の Unity)。**Windows IL2CPP は「結論を出す」ことが条件だったので満たしている**(2026-08-31 に実測して「CI で回さない」と結論した)。判定表は roadmap の M4 節が正本。**v0.3.0 を 2026-09-04 に公開した。** **M4(5 platform)・M5(生成器と校正 API)・M6(Web)の成果が、これで初めて利用者に届いた** —— v0.2.0 以来である。**版番号は当初「v0.3.0 を飛ばして v0.4.0」と書いていたが覆した** —— v0.3.0 という名前で世に出た物が 1 つも無いことを実測したためで、2026-08-31 の下書き(生成物が 1 つも入っていない)と tag は破棄して `03a4557` に打ち直した。**OpenUPM も `0.3.0` を配信している**(公開の約 4 時間後に `dist-tags.latest` が変わった。pipeline は `state=2` / `commit=03a4557` —— **打ち直した tag に紐づいたまま配信された**)。**配布の完了条件 7 件はすべて満たした。****配布の記録は `docs/roadmap.md` の「配布 その 5」にある。** 判定表はいずれも roadmap にある) +## マイルストーン(現在地: **M7(Optional profiles と性能)は完了条件 5 件すべてを満たした**(2026-09-10)。**M7 が最後のマイルストーンである** —— `docs/roadmap.md` の `## M` 見出しは M0 から M7 までで、M8 は無い。**判定が 1 節にまとまっていないのは M7 だけで、5 件が 4 節(`M7a の判定` / `M7b の判定` / `CUDA / cuDNN 同梱の判定` / `M7c の判定`)に分かれている** —— 索引は同文書の `M7 の判定`。**「満たした」と「実証した」は M7 では大きく食い違う** —— `dnn` は実機で 1 度も動かしておらず、`RenderTexture` の画素を運ぶ経路は CI が 1 度も実行せず、CUDA / cuDNN のライセンス条項は読んでいない。**穴の一覧は roadmap の `M7 の判定` にある。ここに写さない。** **M7 の成果はまだどの公開版にも入っていない**(最新の公開版は v0.3.0)。その前の **M6(Web / Wasm)も完了条件 5 件すべてを満たした**(2026-09-03、PR #63 → `1e68d27`)—— **Web が 6 つ目の platform になった**(判定表は `docs/roadmap.md` の M6 節)。**M6 が足した 3 本の check を必須にするかは下の「機構として強制されていること」が持つ**(**ここには書かない** —— 昇格した日に、写した側だけが嘘になる)。**Web にだけ在る制限が 1 つある: `imgcodecs` は JPEG のみで PNG を持たない**(Unity の WebGL 支援が自前の libpng を同梱しているため。他の 5 platform は両方持つ)。その前の **M5(binding specification と generator)も完了条件 5 件すべてを満たした**(2026-09-02)。生成の仕組みは `docs/superpowers/plans/2026-08-31-m5-binding-generator.md` で成立し、**条件 2(`geometry` / `calib` / `features` / `objdetect` の追加)は 3 つの続きの計画で閉じた** —— `objdetect` / `features`(`.superpowers/sdd/2026-09-01-m5-modules-objdetect-features/`)、`geometry`(同上の続き。**リンクは無料だった** —— `features` / `objdetect` の依存として推移的に引かれており `COMPONENTS` に足す必要すら無かった)、カメラの歪み補正(`docs/superpowers/plans/2026-09-01-m5-calib-undistort.md`)、そして **`calib` module と `cv::calibrateCamera`**(`docs/superpowers/plans/2026-09-02-m5-calib-camera.md`)。**最後の 1 つだけが高かった** —— 構成ハッシュが `4785d98e9aad` → `09fcbe260d87` に変わり、5 platform 分の OpenCV を作り直した。**本数は `docs/api-map.md` の冒頭が数える。****M4(Mobile)も完了していない** —— 完了条件 9 件のうち **3 件**が閉じていない(iOS 実機 / lifecycle / macOS 上の Unity)。**Windows IL2CPP は「結論を出す」ことが条件だったので満たしている**(2026-08-31 に実測して「CI で回さない」と結論した)。判定表は roadmap の M4 節が正本。**v0.3.0 を 2026-09-04 に公開した。** **M4(5 platform)・M5(生成器と校正 API)・M6(Web)の成果が、これで初めて利用者に届いた** —— v0.2.0 以来である。**版番号は当初「v0.3.0 を飛ばして v0.4.0」と書いていたが覆した** —— v0.3.0 という名前で世に出た物が 1 つも無いことを実測したためで、2026-08-31 の下書き(生成物が 1 つも入っていない)と tag は破棄して `03a4557` に打ち直した。**OpenUPM も `0.3.0` を配信している**(公開の約 4 時間後に `dist-tags.latest` が変わった。pipeline は `state=2` / `commit=03a4557` —— **打ち直した tag に紐づいたまま配信された**)。**配布の完了条件 7 件はすべて満たした。****配布の記録は `docs/roadmap.md` の「配布 その 5」にある。** 判定表はいずれも roadmap にある) 詳細と完了条件は `docs/roadmap.md` にある。要点のみ: @@ -338,14 +339,14 @@ C++ を選んだ主因は、**sanitizer が安定版ツールチェーンで使 | M4 | Mobile。ここで見つかる制約(stripping、static link、16 KB page size)が M5 の生成コードの形を規定する。**16 KB 対応は 2027-02-01 から Google Play の要件**(それより前に満たす。止まるのは利用者のリリースである)。macOS の `.meta` 実測と Windows IL2CPP の結論もここ | | M5 | binding specification と generator。**`imgcodecs` は M3.5 で出した**(`ocvu_imencode` / `ocvu_imdecode`、2026-08-30)ので、ここには残っていない。**生成の仕組みは 2026-09-01 に成立し、完了条件 4 件を満たした。残る 1 件は新しい OpenCV module(`geometry` / `calib` / `features` / `objdetect`)の追加**で、別の subsystem(`COMPONENTS` / `Modules` / notice / 成果物の大きさ / 依存 allowlist が同時に動く)なので当初は次の計画に分けた —— 混ぜると「生成が壊れたのか module が壊れたのか」が切り分けられない。**続く 3 つの計画で条件 2 を閉じた** —— `objdetect`(QR の符号化・復号)と `features`(ORB 検出)、`geometry`(射影変換の推定)、カメラの歪み補正(`ocvu_undistort` / `ocvu_find_chessboard_corners`)、そして **`calib` module と `cv::calibrateCamera`**(2026-09-02。**構成ハッシュが変わり 5 platform 分を作り直した**)。**これで 5 件すべてを満たした。** 判定は roadmap の M5 節 | | **M6** | **Web / Wasm(Unity 同梱 Emscripten と整合)—— 完了条件 5 件すべてを満たした**(2026-09-03)。**Web は 6 つ目の platform で、クロスコンパイルと静的ライブラリの両方**(iOS の 2 つの性質を同時に持つ)。**このマイルストーンの価値は、Web でしか出ない欠陥を 7 件捕まえたことにある** —— うち 1 件は **`CLAUDE.md` の中核の不変条件(例外を ABI の外へ出さない)が Web でだけ黙って成立していなかった**もので、**ブラウザで OpenCV が実際に投げるまで誰も知らなかった**(Emscripten は既定で C++ 例外を無効にする。`throw` は残るのに `catch` が 1 つも組み込まれない)。**Web にだけ在る制限**: `imgcodecs` は JPEG のみ。判定表は roadmap の M6 節 | -| M7 | Optional profiles と性能。**OpenCV 5 の新しい DNN エンジンはここ** —— 差になりうるのは**新しいエンジンのほう**であって、**「競合に DNN が無い」ではない**(Enox は OpenCV DNN と Unity Sentis を切り替える仕組みを持つ。ただし OpenCV 4.13 系なので 5 の新エンジンではない。[競合調査](docs/unity-opencv-integration-research-and-plan.md) §4.2)。Unity には推論の代替があるので前倒ししない。**2026-08-30 の上流調査で「5.0 で DNN を作り込むと 5.1 で作り直しになる」根拠が付き、`dnn` を足す前に native bridge を module 単位に分ける決定を入れた。** 決定の内訳・前提条件・一次情報は roadmap の M7 節にある(**ここに再掲しない** —— 2 箇所が同時に古くなる) | +| **M7** | **Optional profiles と性能 —— 完了条件 5 件すべてを満たした**(2026-09-10。判定は roadmap の M7a / M7b / CUDA / M7c の 4 節に分かれ、索引は `M7 の判定`)。**`dnn` は opt-in profile として出した**(`OCVU_PROFILE_DNN`。native binary には既定で入っており、切っているのは C# の assembly だけである)。**OpenCV 5 の新しい DNN エンジンはここ** —— 差になりうるのは**新しいエンジンのほう**であって、**「競合に DNN が無い」ではない**(Enox は OpenCV DNN と Unity Sentis を切り替える仕組みを持つ。ただし OpenCV 4.13 系なので 5 の新エンジンではない。[競合調査](docs/unity-opencv-integration-research-and-plan.md) §4.2)。~~Unity には推論の代替があるので前倒ししない。~~ **2026-08-30 の上流調査で「5.0 で DNN を作り込むと 5.1 で作り直しになる」根拠が付き、`dnn` を足す前に native bridge を module 単位に分ける決定を入れた。** その決定は **2026-09-08 に所有者が解除した** —— **上流の実測(旧エンジンの削除・`enum EngineType` の再番号)はいまも成立しており、5.1 が来たら作り直しが要ると見込んでよい。** 変わったのは、所有者がその費用を承知のうえで受け入れると判断したことだけである。決定の内訳・前提条件・一次情報・解除の経緯は roadmap の M7 節にある(**ここに再掲しない** —— 2 箇所が同時に古くなる) | **M2 以降の完了条件には、native 単体テストだけでなく実際の Unity Player から C# → P/Invoke → C ABI → OpenCV を通る smoke test を含める。** M2 はこれをローカルで満たしたうえで、2026-08-29 に CI(Linux)でも実行して条件 7 を満たした。**ただし「CI で走っている」は「赤ければ止まる」ではない** —— `ci-unity` が merge を止めるかどうかは下記「機構として強制されていること」を見ること。 ## 実装に着手するとき 1. `docs/roadmap.md` で対象マイルストーンの目的・ゴール・完了条件・**非ゴール**を確認する -2. 実装計画があればそれに従う。M0 の計画は `docs/superpowers/plans/2026-08-25-m0-tdd-harness.md`、M1 の計画は `docs/superpowers/plans/2026-08-25-m1-opencv-build.md`、M2 の計画は `docs/superpowers/plans/2026-08-26-m2-windows-vertical-slice.md`(いずれも実施済み。M2 は完了条件 8 件すべてを満たした)、M3 の計画は `docs/superpowers/plans/2026-08-28-m3-desktop-three-platforms.md`(Task 8 まで実施済み。完了条件 6 件すべて達成。v0.1.0 を公開し、Linux の欠陥を直した v0.1.1 を出した)、M3.5 の計画は `docs/superpowers/plans/2026-08-30-m3.5-distribution-shape.md`(実施済み。**完了条件 6 件をすべて満たした** —— 判定は `docs/roadmap.md` の M3.5 節)、M4 の計画は `docs/superpowers/plans/2026-08-30-m4-mobile.md`(実施済み。**9 件中 6 件**。件数の正本は roadmap の M4 判定表)、M5 の計画は `docs/superpowers/plans/2026-08-31-m5-binding-generator.md`(実施済み。**5 件中 4 件。条件 2 は計画の側で意図的に外してある**)、その条件 2 の前半を出した計画は `docs/superpowers/plans/2026-09-01-m5-modules-objdetect-features.md`(実施済み。**`objdetect` / `features`、続けて `geometry` を出して条件 2 は「部分的に満たした」になった。`calib` は出していない**)。続けてカメラの歪み補正を出した計画は `docs/superpowers/plans/2026-09-01-m5-calib-undistort.md`(実施済み。`ocvu_undistort` / `ocvu_find_chessboard_corners` を `calib` module を足さずに出した)、**条件 2 を閉じた計画は `docs/superpowers/plans/2026-09-02-m5-calib-camera.md`**(実施済み。**`calib` module を足し `cv::calibrateCamera` を出した。5 件すべてを満たした**)。**M6 の計画は `docs/superpowers/plans/2026-09-03-m6-web-wasm.md`**(実施済み。**Web を 6 つ目の platform として足し、完了条件 5 件すべてを満たした**)。M7 の計画はまだ無いので、`superpowers:writing-plans` で先に書く +2. 実装計画があればそれに従う。M0 の計画は `docs/superpowers/plans/2026-08-25-m0-tdd-harness.md`、M1 の計画は `docs/superpowers/plans/2026-08-25-m1-opencv-build.md`、M2 の計画は `docs/superpowers/plans/2026-08-26-m2-windows-vertical-slice.md`(いずれも実施済み。M2 は完了条件 8 件すべてを満たした)、M3 の計画は `docs/superpowers/plans/2026-08-28-m3-desktop-three-platforms.md`(Task 8 まで実施済み。完了条件 6 件すべて達成。v0.1.0 を公開し、Linux の欠陥を直した v0.1.1 を出した)、M3.5 の計画は `docs/superpowers/plans/2026-08-30-m3.5-distribution-shape.md`(実施済み。**完了条件 6 件をすべて満たした** —— 判定は `docs/roadmap.md` の M3.5 節)、M4 の計画は `docs/superpowers/plans/2026-08-30-m4-mobile.md`(実施済み。**9 件中 6 件**。件数の正本は roadmap の M4 判定表)、M5 の計画は `docs/superpowers/plans/2026-08-31-m5-binding-generator.md`(実施済み。**5 件中 4 件。条件 2 は計画の側で意図的に外してある**)、その条件 2 の前半を出した計画は `docs/superpowers/plans/2026-09-01-m5-modules-objdetect-features.md`(実施済み。**`objdetect` / `features`、続けて `geometry` を出して条件 2 は「部分的に満たした」になった。`calib` は出していない**)。続けてカメラの歪み補正を出した計画は `docs/superpowers/plans/2026-09-01-m5-calib-undistort.md`(実施済み。`ocvu_undistort` / `ocvu_find_chessboard_corners` を `calib` module を足さずに出した)、**条件 2 を閉じた計画は `docs/superpowers/plans/2026-09-02-m5-calib-camera.md`**(実施済み。**`calib` module を足し `cv::calibrateCamera` を出した。5 件すべてを満たした**)。**M6 の計画は `docs/superpowers/plans/2026-09-03-m6-web-wasm.md`**(実施済み。**Web を 6 つ目の platform として足し、完了条件 5 件すべてを満たした**)。**M7 の計画は 4 本ある** —— `docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md`(**仕様であって計画ではない。** 3 本が共有する正本)、`-m7a-low-copy-and-benchmarks.md`(実施済み。完了条件 2・3)、`-m7b-module-separation.md`(実施済み。完了条件 4)、`-m7c-dnn-profile.md`(実施済み。完了条件 1。**冒頭の「着手してよいという合図ではない」は 2026-09-08 の解除で失効している**)。**完了条件 5 は計画を経ない文書上の決定として閉じた**(roadmap の `CUDA / cuDNN 同梱の判定`)。**M7 が最後のマイルストーンなので、次の計画は無い。** 3. 計画は**マイルストーンごとに 1 つ**書く。各計画は単独で動作・テスト可能なソフトウェアを produce すること M2 以降で確定が必要な残りの事項(計画書 §12 のうち未決定分): OpenCV 5.0.0 の固定期間と 5.x update policy、ライセンス表示と SBOM の公開フロー、各 platform で必須とする Editor / Mono / IL2CPP / device test matrix。 @@ -413,7 +414,7 @@ CI が赤くて直した場合、その修正差分にもレビューが要る | --- | --- | --- | | main の branch protection | 有効 | 直接 push は `GH006` で拒否される | | 必須チェック | **21 本。** `ci-native` の 3 job(`Windows x64 (L1 + L3)` / `macOS arm64 (L1 + L3)` / `Linux x64 (L1 + L3)`)、`ci-sanitizers` の 2 job(`Windows x64 AddressSanitizer (L2)` / `Linux x64 ASan+LSan (L2)`)、`ci-lint` の 4 job(`Workflows (actionlint)` / `Shell scripts (shellcheck)` / `PowerShell (PSScriptAnalyzer)` / `Documentation links`)、`codeql` の 2 job(`Analyze c-cpp` / `Analyze csharp`)、`ci-unity` の 2 job(`Unity EditMode (Linux)` / `Unity Standalone (Linux)`)、**`release` の 6 job(`Package windows-x64` / `macos-arm64` / `linux-x64` / `android-arm64` / `ios-arm64` と `Assemble the release assets`)、`ci-native` のモバイル 2 job(`Android arm64-v8a (cross-build)` / `iOS arm64 (cross-build)`)**。2 本 → 5 本(M3 完了時)→ 13 本(2026-08-29、PR #30 が 13 本すべてを緑にしたのを見てから拡張)→ **21 本(2026-08-31。`release.yml` を PR でも走らせるようにして 6 本、モバイルのクロスビルド 2 本を足した。どちらも main と PR の両方で安定して緑になったのを見てから拡張した)**。2026-08-31 に API で実測し、この表と一致 | 全部 pass しないと merge できない | -| 必須でないもの | **`ci-unity` の `Plugin ` 5 本、`release` の `Publish the release` の 1 本、そして M6 が足した 3 本(`ci-native` の `Web wasm32 (cross-build)` / `ci-unity` の `Web browser E2E` / `release` の `Package web-wasm`)。** **M6 の 3 本を必須にしないのは、緑の実績がまだ浅いからである** —— **2026-09-03 に PR #63 で 3 本とも初めて緑になり、merge 後の main でも `Web wasm32 (cross-build)` と `Web browser E2E` が緑になった**(`Package web-wasm` は `release.yml` が main の push で走らないので PR の 1 回だけである)。それでも `CLAUDE.md` の規律は「**安定して緑になったのを見てから必須へ加える**」で、**PR 1 回 + main 1 回は「安定して」ではない。** **モバイルの 2 本も M4 の直後は必須外で、2026-08-31 に昇格した。** 同じ手順を踏む(**PR と main で安定して緑になったら昇格する**)。**帰結を直視すること: いま Web のレーンは赤くても merge を止めない。****`Plugin` が 4 本から 5 本になった**のは web-wasm が加わったためである。 `Plugin` は落ちると `unity` job が `if: !cancelled()` で走って材料が無いまま赤くなるので、必須チェックである Unity の 2 レーンがそのまま止める(**skip は required check を通す**ので `needs:` だけでは止まらない —— そこは workflow の構造で塞いである)。**モバイルの 2 本は 2026-08-31 に必須へ昇格した** —— 当初の理由(この構成の CI がまだ 1 度も緑になっていない)が失効し、M4 以降 PR と main で安定して緑になったためである。**`Publish the release` は必須にしない** —— PR では常に skip される job で、**GitHub は skip を合格として通す**ので、必須にしても何も止めない。「見ているが止めない」の典型である。`build-opencv` / `nightly` は PR では起動しない(構成変更・定期実行が trigger)ので、そもそも必須にできない | — | +| 必須でないもの | **`ci-unity` の `Plugin ` 5 本、`release` の `Publish the release` の 1 本、そして M6 が足した 3 本(`ci-native` の `Web wasm32 (cross-build)` / `ci-unity` の `Web browser E2E` / `release` の `Package web-wasm`)。** **M6 の 3 本を必須にしない理由として「緑の実績がまだ浅い」と書いていたが、その根拠は失効した。** 2026-09-10 に実測した実績は次のとおり: **`Web wasm32 (cross-build)` / `Web browser E2E` / `Package web-wasm` の 3 本とも、PR #63 / #69 / #70 / #72 / #73 の 5 本すべてで success**。加えて **main の直近 6 run でも `Web wasm32 (cross-build)` と `Web browser E2E` が success** である(`Package web-wasm` は `release.yml` が main の push で走らないので、実績は PR 側だけである)。**したがって「浅いから」はもう理由にならない。** それでも必須へ昇格させていないのは、**昇格が GitHub 側の設定変更であって文書修正ではなく、リポジトリ所有者の判断だからである** —— **これは未決の判断として開いている。** モバイルの 2 本が M4 の直後は必須外で 2026-08-31 に昇格したのと同じ手順を踏む。**帰結を直視すること: いま Web のレーンは赤くても merge を止めない。****`Plugin` が 4 本から 5 本になった**のは web-wasm が加わったためである。 `Plugin` は落ちると `unity` job が `if: !cancelled()` で走って材料が無いまま赤くなるので、必須チェックである Unity の 2 レーンがそのまま止める(**skip は required check を通す**ので `needs:` だけでは止まらない —— そこは workflow の構造で塞いである)。**モバイルの 2 本は 2026-08-31 に必須へ昇格した** —— 当初の理由(この構成の CI がまだ 1 度も緑になっていない)が失効し、M4 以降 PR と main で安定して緑になったためである。**`Publish the release` は必須にしない** —— PR では常に skip される job で、**GitHub は skip を合格として通す**ので、必須にしても何も止めない。「見ているが止めない」の典型である。`build-opencv` / `nightly` は PR では起動しない(構成変更・定期実行が trigger)ので、そもそも必須にできない | — | | strict | 有効 | ブランチが main より古いと merge できない(`allow_update_branch` で自動更新可) | | 必須レビュー数 | **0** | PR は必須だが人間の承認は不要 | | enforce_admins | **有効** | 管理者も例外ではない。抜け道は無い | @@ -474,11 +475,21 @@ CodeQL まで見る。それでも次は緑のまま通過する。 - **必須でないレーンが赤いこと。** どのレーンがそれに当たるかはここには 書かない —— 上の「機構として強制されていること」の表が唯一の記載場所で、 正本はさらにその先の GitHub 側の設定である -- **schedule で起動する検査。** `nightly.yml` は cron(毎日 04:00 UTC)で - 走る前提で書いてあるが、**cron の経路はまだ 1 度も動いていない** - (手動起動が 2 回あるだけで、1 回目は 4 job 中 3 job が失敗した)。 - 「ファイルが存在する」は「CI で実行された」ではない —— M2 の条件 7 を - そう判定したのと同じ基準を、こちらにも当てる +- **`OCVU_PROFILE_DNN` を立てた Unity。** **どの workflow も、どの `dev.ps1` の + レーンも、この define を立てない**(2026-09-10 に `.github/` と `tools/` を + grep して 0 件)。したがって **`CvUnity.Interop.Dnn` / `CvUnity.Dnn` が Unity の + 中で実際にコンパイルされ、IL2CPP の stripping が dnn の P/Invoke 宣言を + 消さないことは、人が手で 1 回確かめたきりである** —— 次に dnn の ABI が + 変わっても自動では再検証されない(M7c)。**「dnn は CI が見ていない」と + 丸めないこと。** CI が実際に見ているものは 4 つある: L1 の GoogleTest、 + L3(`DnnInferenceTests` が実物の DLL を P/Invoke で叩く)、netstandard2.1 の + shim が `Runtime/Interop.Dnn` と `Runtime/Dnn` を**常に**コンパイルすること + (define を見ない。`CvUnity.Runtime.Shim.csproj` にそう書いてある)、そして + `tools/tests/NonStandardProfileCompile.Tests.ps1` が非既定 profile の生成物を + `dotnet build` に通すこと(`test-tools-slow` → `ci-native` の desktop 3 job。 + **ただし通しているのは合成した `dnnprobe` spec の出力であって、実物の + `NativeMethods.Dnn.g.cs` ではない**)。**穴は「Unity と IL2CPP の側」に + 限られており、そこは丸ごと空いている。** - **macOS 上の Unity の挙動**(CI の macOS job は plugin をビルドするが Unity を 起動しない)。**2026-08-31 に 4 回試して「CI では埋まらない」と確定した** —— game-ci は macOS を支えず(`darwin-platform is not supported`)、Unity Hub で @@ -497,7 +508,7 @@ CodeQL まで見る。それでも次は緑のまま通過する。 ものである** —— Windows 固有の IL2CPP の欠陥だけが CI に映らない - **全部入りの package。** 3 platform を 1 つに束ねる形自体は M3.5 で成立したが、 **それを確かめるレーン(`dev.ps1 test-unity-tarball`)はどの workflow からも - 走っていない。** このレーンの実測はローカル(Windows)だけである(**6 platform の全部入りでは 2 回走らせてある** —— 2026-09-03 に `release.yml` の空撃ちが作った tarball を材料に、2026-09-04 に公開する下書きの asset そのものを材料に。どちらも `native plugins present: 6` / `34 passed` / exit 0。詳細は `docs/roadmap.md` の「配布 その 5」) + 走っていない。** このレーンの実測はローカル(Windows)だけである(**6 platform の全部入りでは 2 回走らせてある** —— 2026-09-03 に `release.yml` の空撃ちが作った tarball を材料に、2026-09-04 に公開する下書きの asset そのものを材料に。どちらも `native plugins present: 6` / exit 0(**件数を写さない** —— 正本は上の `test-unity-tarball` の行で、そちらは 2026-09-10 に取り直してある。当時の値をここに残すと、同じファイルの中に 2 つの件数が並ぶ)。詳細は `docs/roadmap.md` の「配布 その 5」) - **mobile 実機の挙動。** Android / iOS は CI がビルドし、全部入りにも入るが、 **実機で動かした実績が 1 度も無い**(M4 の完了条件 3 件が閉じていない。 実機が要る 2 件は `docs/m4-device-verification.md` の手順書に落としてある) @@ -549,17 +560,17 @@ CodeQL まで見る。それでも次は緑のまま通過する。 | skill | いつ使うか | | --- | --- | -| `add-abi-function` | `ocvu_` の ABI 関数を追加・変更・削除するとき。L1 → **spec に 1 エントリ + `dev.ps1 generate`** → 実装 → L3 → status 同期の TDD 順序と、所有権・バッファ・例外バリアの規約。**M5 以降、宣言は手で書かない** —— ヘッダにも `NativeMethods.cs` にも手で足さない(足せば `verify-generated` が落とす)。**新しい OpenCV module を足すなら、先にリンクを実証する節**も読むこと —— `COMPONENTS` に足すだけでは binary は 1 バイトも増えず、`cv::` を実際に参照する L1 テストだけが証拠になる。**2026-09 の API 拡張で 1 つ加わった** —— **`docs/api-reference.md` に名前を書くのも同じ commit である**(速いレーンが「spec の全関数名が api-reference に現れること」を fail-fast で見るので、**「文書は最後にまとめる」は成立しない**)| -| `add-a-platform` | **対象 platform を足す・外す・変えるとき。** 一覧を持つ場所は **少なくとも 17 箇所**ある(**数を写さない。正本は skill 側で、そこには「この数も古い」と注記がある** —— M6 で数え直したら足りず、**リポジトリの外にもあった**)、語彙が違うので grep 1 回では揃わない。`.meta` のキー名 / ファイル名の platform 間衝突 / クロスでの `find_package` の閉じ込め / 静的ライブラリの依存の束ね / 新しい third-party など、**クロスビルドが緑になってから CI で 8 回落ちた**罠を、踏んだ順に並べてある | -| `milestone-complete` | マイルストーンの完了を判定するとき。roadmap の完了条件との実測照合、**文書の陳腐化確認**、CI での確定。**M4 で 2 つ加わった** —— 実機が要る条件は「満たすが未実証」ですらなく人が実行する手順書に落とす / **必須チェックの充足は成功件数ではなく名前で突き合わせる**(GitHub は `skipped` と `neutral` も pass として通す)。**M6 でさらに 3 つ** —— **条件つきで先送りした手順書は、条件が満たされた瞬間にその否定文が嘘になる**(しかも中の「済ませた」印は語句の置換では直らず、**そのまま実行すると中身と説明が食い違った Release が出る**)/ **数が動かなくてもその補集合が動く**(M6 は**必須チェックの本数を 1 本も変えずに、必須でないものの本数を変えた**。数が動いていないので**どの機械も捕まえない**)/ **直すときは強いほうから**(要約や図は目に付くので先に直り、**実行手順と散文が残る**)。**2026-09 の API 拡張でもう 1 つ** —— **`.md` だけを洗うと足りない**(事実は検査スクリプトと実装のコメントにも書かれており、そちらは「文書」と呼ばれないので見落とす。**実際に 4 件踏んだ。どれも CI は緑のまま通る**)| -| `prove-a-check-works` | テスト・assertion・検証スクリプト・allowlist・CI ゲート・hook を足すか変えるとき。**壊して落ちることを見るまで、その検査は動くと言えない。** M1 の全タスクが同じ欠陥(著者が列挙した形だけを見る)を生んだので手順にした。**M4 で 6 つの節が加わった** —— 壊す前にコミットする / 置換が空振りしていないか確かめる / 手前に別の門があると番人に到達しない / 述語のゆるさ(構造的に常に真・部分一致・大文字小文字・散文に当たる)/ **自分でパースする検査は本物の解釈を代理できない** / **正本を写さず正本から読む**。**M5 でさらに 4 つ** —— **Unity 経由のクラッシュは赤いテストにならない**(無音で 10 分以上返り、結果が 1 バイトも出ない)/ 手前の門の 2 例目(`System.Text.Json` が先に落とすので、足した検査は 1 度も動いていなかった)/ **アンカーの意味**(.NET の `$` は末尾の `\n` の直前にも当たる。「一致したか」ではなく**値全体を覆ったか**を見る)/ **列挙に基づく門を出口側の構造に基づく門へ変える**(ただし下流に検査が無い所にだけ足す)。**M6 でもう 1 つ** —— **負の対照の「形」が想定と違うことがある**(`-msimd128` を外すと検査が赤くなるのではなく**コンパイルが通らない**。より強い形だが**同じ形ではない**ので、同じ言葉で記録してはいけない ——「壊したら落ちた」と書くと次の人は「検査が落ちた」と読むが、**検査は 1 度も走っていない**)。**2026-09 の API 拡張でその対になる形が出た** —— **負の対照がそもそも取れないことがある**(ステレオの入力を縞から模様に変えたとき、BM 側は縞に戻すと落ちたが **SGBM は縞でも視差を見つけるので落ちなかった**)。**M6 のものが「より強い形」なら、こちらは「弱い形」である** ——「壊したら落ちた」と書けないときは、書けないと書く | +| `add-abi-function` | `ocvu_` の ABI 関数を追加・変更・削除するとき。L1 → **spec に 1 エントリ + `dev.ps1 generate`** → 実装 → L3 → status 同期の TDD 順序と、所有権・バッファ・例外バリアの規約。**M5 以降、宣言は手で書かない** —— ヘッダにも `NativeMethods.cs` にも手で足さない(足せば `verify-generated` が落とす)。**新しい OpenCV module を足すなら、先にリンクを実証する節**も読むこと —— `COMPONENTS` に足すだけでは binary は 1 バイトも増えず、`cv::` を実際に参照する L1 テストだけが証拠になる。**2026-09 の API 拡張で 1 つ加わった** —— **`docs/api-reference.md` に名前を書くのも同じ commit である**(速いレーンが「spec の全関数名が api-reference に現れること」を fail-fast で見るので、**「文書は最後にまとめる」は成立しない**)。**M7 で 3 つ加わった** —— **`OCVU_SOURCES` はもう手で書く一覧ではない**(正本は `native/modules.cmake` の `OCVU_MODULE_`。新しい module では `#include` 1 行に加えてここに 2 行要る)/ **profile を持つ module に足すときの分岐**(出力先・クラス名・assembly が変わり、**到達性テストの呼び出し元を自分で作らないと生成物が stripping の対象になる**。CI はどのレーンも define を立てないので確認は手動)/ **逆向きの検査は 2 本**(ソースを読む `BindingGenerator.Tests.ps1` と、binary の export 面を読む `verify-exported-symbols.ps1`)| +| `add-a-platform` | **対象 platform を足す・外す・変えるとき。** 一覧を持つ場所は**多い**(**数を写さない。正本は skill 側の表の行数である** —— skill からも数を落とした。**M7 で 2 つ増えたのに「17」だけが取り残された**ため。M6 で数え直したときは**リポジトリの外にもあった**)、語彙が違うので grep 1 回では揃わない。`.meta` のキー名 / ファイル名の platform 間衝突 / クロスでの `find_package` の閉じ込め / 静的ライブラリの依存の束ね / 新しい third-party など、**クロスビルドが緑になってから CI で 8 回落ちた**罠を、踏んだ順に並べてある。**M7 で 3 つ加わった** —— `release.yml` の「未知の platform は落とす」門は **2 つではなく 3 つ**(M7b が公開面の検査を足した)/表に 2 行(`verify-exported-symbols.ps1` の配線 2 箇所、全部入り tarball の上限 —— **M7c は実際にここで落ちた**)/**構成ハッシュは `opencv-config.psd1` の内容しか見ないので、toolchain file に書いた flag は復元済み artifact に反映されない**(罠 8 の対になる半分)。あわせて **allowlist の限界**(別のライブラリへ静的に取り込まれた third-party は見えない)も罠 5 に入れた | +| `milestone-complete` | マイルストーンの完了を判定するとき。roadmap の完了条件との実測照合、**文書の陳腐化確認**、CI での確定。**M4 で 2 つ加わった** —— 実機が要る条件は「満たすが未実証」ですらなく人が実行する手順書に落とす / **必須チェックの充足は成功件数ではなく名前で突き合わせる**(GitHub は `skipped` と `neutral` も pass として通す)。**M6 でさらに 3 つ** —— **条件つきで先送りした手順書は、条件が満たされた瞬間にその否定文が嘘になる**(しかも中の「済ませた」印は語句の置換では直らず、**そのまま実行すると中身と説明が食い違った Release が出る**)/ **数が動かなくてもその補集合が動く**(M6 は**必須チェックの本数を 1 本も変えずに、必須でないものの本数を変えた**。数が動いていないので**どの機械も捕まえない**)/ **直すときは強いほうから**(要約や図は目に付くので先に直り、**実行手順と散文が残る**)。**2026-09 の API 拡張でもう 1 つ** —— **`.md` だけを洗うと足りない**(事実は検査スクリプトと実装のコメントにも書かれており、そちらは「文書」と呼ばれないので見落とす。**実際に 4 件踏んだ。どれも CI は緑のまま通る**)。**M7 でさらに 3 つ** —— CI に配線が無いレーン(`test-unity-graphics` / `benchmark`)は**判定のときだけが唯一の実行機会**である/`test-unity-player` は後始末でハングする既知の欠陥を持つ(結果 XML には出るので、プロセスを止めて `assert-unity-results.ps1` を直接掛ける)/**判定を複数の節に割ったら、残数を書かず、名指し参照を自分で洗う**(`ci-lint` のリンク検査は backtick 見出し参照を見ない。**索引になる節を 1 つ置かないと「完了した」と書いた文がどこにも無い状態になる** —— M7 で実際にそうなった)| +| `prove-a-check-works` | テスト・assertion・検証スクリプト・allowlist・CI ゲート・hook を足すか変えるとき。**壊して落ちることを見るまで、その検査は動くと言えない。** M1 の全タスクが同じ欠陥(著者が列挙した形だけを見る)を生んだので手順にした。**M4 で 6 つの節が加わった** —— 壊す前にコミットする / 置換が空振りしていないか確かめる / 手前に別の門があると番人に到達しない / 述語のゆるさ(構造的に常に真・部分一致・大文字小文字・散文に当たる)/ **自分でパースする検査は本物の解釈を代理できない** / **正本を写さず正本から読む**。**M5 でさらに 4 つ** —— **Unity 経由のクラッシュは赤いテストにならない**(無音で 10 分以上返り、結果が 1 バイトも出ない)/ 手前の門の 2 例目(`System.Text.Json` が先に落とすので、足した検査は 1 度も動いていなかった)/ **アンカーの意味**(.NET の `$` は末尾の `\n` の直前にも当たる。「一致したか」ではなく**値全体を覆ったか**を見る)/ **列挙に基づく門を出口側の構造に基づく門へ変える**(ただし下流に検査が無い所にだけ足す)。**M6 でもう 1 つ** —— **負の対照の「形」が想定と違うことがある**(`-msimd128` を外すと検査が赤くなるのではなく**コンパイルが通らない**。より強い形だが**同じ形ではない**ので、同じ言葉で記録してはいけない ——「壊したら落ちた」と書くと次の人は「検査が落ちた」と読むが、**検査は 1 度も走っていない**)。**2026-09 の API 拡張でその対になる形が出た** —— **負の対照がそもそも取れないことがある**(ステレオの入力を縞から模様に変えたとき、BM 側は縞に戻すと落ちたが **SGBM は縞でも視差を見つけるので落ちなかった**)。**M6 のものが「より強い形」なら、こちらは「弱い形」である** ——「壊したら落ちた」と書けないときは、書けないと書く。**M7 で 5 つ** —— **数を見る検査は、誤った読みに満たされる**(同じ形を 3 回作った実例。フォルダを「2 つ以上読めたか」で守ると 4 つのうち 2 つしか読めない誤読が通り、**合計 3 本に増やしても釣り合わせられた**。閉じたのは「合計は十分に大きいか」を「**フォルダを出すはずだった要素は全部出したか**」に変えたときで、**その構造の門 1 本が数の門 2 本を消した**)/**門を何本置くかは本数ではなく、1 本ずつ単独で何を捕まえるかで決める**(対になって初めて 1 つの門になる形と、束にしても釣り合う形の見分け方)/**検査が当たっていた対象が本番の物ではなく代用物だった** 2 例(開発用 binary / 手書き probe)/**負の対照の 3 例目。壊しても落ちず、かつ必要性を確定させる経路がリポジトリに存在しない**(`.clone()`。「検査が働くことを示せていない」と「そのコードが要ることを示せていない」は別の主張である)/**走らないレーンは直した後も同じ速さで腐る**(`test-unity-tarball` が M7a で再発した)| **hook**(`.claude/settings.json`) | hook | 契機 | 動作 | | --- | --- | --- | -| `block-bulk-git-add.sh` | PreToolUse (Bash/PowerShell) | `git add -A` / `git add .` を**拒否**。連結コマンド内も見る | -| `check-unityengine-leak.sh` | PostToolUse (Write/Edit) | `Runtime/Interop` と `Runtime/Core` への `UnityEngine` 混入を指摘 | +| `block-bulk-git-add.sh` | PreToolUse (Bash/PowerShell) | `git add -A` / `git add .` を**拒否**。連結コマンド内も見る。**`git add -u` と `git commit -a` は通す** —— 生成物が履歴に入るのを止めるのは `check-staged-generated-file.sh` の担当で、分担は意図的である(冒頭のコメントが「生成物、一時的なデバッグ変更を巻き込む」と書きながらその 2 つを拾う `-u` を見ていなかったずれは、2026-09-10 にコメント側を直した)| +| `check-unityengine-leak.sh` | PostToolUse (Write/Edit) | **shim がコンパイルする Runtime のフォルダ**への `UnityEngine` 混入を指摘。**一覧を写さず正本から読む** —— 正本は `tests/Managed/CvUnity.Runtime.Shim/CvUnity.Runtime.Shim.csproj` の `` 要素のうち **Include が `$(OcvuPackageRuntime)` を根に持つもの**である(リテラルの相対パスで書かれたフォルダは対象外。hook の冒頭が何を除いているかを書いている)。**判定は合計ではなく要素ごとである** —— 合計 3 本の突き合わせは**釣り合わせられる**と実測で分かった(3 パスを持つ要素と 1 つも読めない要素を並べると要素 2 / パス 3 / フォルダ 3 で成立し、1 フォルダが黙って無防備になる)。**写していた頃は実際に古くなった**: M7c が profile 分離で `Runtime/Interop.Dnn` と `Runtime/Dnn` を足し、shim は 4 フォルダをコンパイルするようになったのに hook は 2 のままで、**後の 2 つは素通りしていた**(2026-09-10 に 4 フォルダへ probe を当てて実測し、正本から読む形に直した)| | `check-exception-barrier.sh` | PostToolUse (Write/Edit) | `ocvu_status` を返す `extern "C"` 関数の `OCVU_TRY_BEGIN` 囲い忘れを指摘 | | `check-powershell-encoding.sh` | PostToolUse (Write/Edit) | 非 ASCII を出力する `.ps1` / `.psm1` の `[Console]::OutputEncoding` 未設定を指摘。M1 で 3 つの別々のスクリプトに順に現れた | | `check-assertions-reachable.sh` | PostToolUse (Write/Edit) | `*.Tests.ps1` で、終了コードを決めた後ろに置かれ**落ちようがない** assertion を指摘 | diff --git a/README.ja.md b/README.ja.md index 7971c69..3b24332 100644 --- a/README.ja.md +++ b/README.ja.md @@ -4,7 +4,7 @@ OpenCV 5 を、このプロジェクトが所有する C ABI 越しに Unity へ [English](README.md) -> **現状: 公開済みの最新版は v0.3.0 です(2026-09-04)。これを書いた時点では、リポジトリと公開版は揃っています** —— 以下はその両方の説明です。その後リポジトリが先に進んだかどうかの正本は、[ロードマップ](docs/roadmap.md)の「配布」の節であって、ここではありません。 **6 platform を配る最初の版**であり(Windows x64 / macOS arm64 / Linux x64 / Android arm64-v8a / iOS arm64 / Web (WebGL))、**生成される binding 層とカメラ校正を含む最初の版**でもあります。どの platform も CI がビルド・テスト・パッケージ化し、Unity 自身が Mono(EditMode)・実物の IL2CPP Player・headless のブラウザでプラグインを動かしています。**正直な限界を先に書きます。** **Android と iOS は実機で一度も動かしていません** —— CI はクロスコンパイルして成果物を検査しますが、どの端末もこの binary を読み込んだことがありません。**Web には PNG がありません**(`imgcodecs` は JPEG のみ。下記)。動かしたブラウザも Linux の headless Chromium 1 つだけです。**macOS 上で Unity を起動したこともありません**(`.meta` の解釈は他の OS の Unity に問うて確認済みです)。**公開している C ABI は意図的に狭くしてあります** —— 面積を覆うことではなく、所有権・stride・エラー処理・IL2CPP・platform の振り分けを正しくすることを目的にしているからです。**Linux では v0.1.1 以降を使ってください** —— v0.1.0 の Linux プラグインは glibc 2.38 を要求し、Ubuntu 22.04 では読み込めませんでした。 +> **現状: 公開済みの最新版は v0.3.0 です(2026-09-04)。リポジトリはその先へ大きく進んでいます。** 以下はその公開版ではなく、リポジトリの説明です。**v0.3.0 は tag `03a4557` で、2026-09 の API 拡張よりも M7 よりも前の版です** —— 差は「いくつかの機能」ではなく、それ以降に入った全部です。両端とも生成物から取れる唯一の数字を挙げると、`docs/api-map.md` は **v0.3.0 で公開 C ABI 27 本、ここでは 57 本**と書いています。C# 側では `CvAruco` / `CvCoreOps` / `CvStereo` / `CvDnn` / `RenderTextureConverter` がその tag には存在しないので、v0.3.0 に対して `OCVU_PROFILE_DNN` を立ててもその先には何もありません。**実際に配られたものの正本は[ロードマップ](docs/roadmap.md)の「配布」の節であって、この段落ではありません。** **6 platform を配る最初の版**であり(Windows x64 / macOS arm64 / Linux x64 / Android arm64-v8a / iOS arm64 / Web (WebGL))、**生成される binding 層とカメラ校正を含む最初の版**でもあります。どの platform も CI がビルド・テスト・パッケージ化し、Unity 自身が Mono(EditMode)・実物の IL2CPP Player・headless のブラウザでプラグインを動かしています。**正直な限界を先に書きます。** **Android と iOS は実機で一度も動かしていません** —— CI はクロスコンパイルして成果物を検査しますが、どの端末もこの binary を読み込んだことがありません。**Web には PNG がありません**(`imgcodecs` は JPEG のみ。下記)。動かしたブラウザも Linux の headless Chromium 1 つだけです。**macOS 上で Unity を起動したこともありません**(`.meta` の解釈は他の OS の Unity に問うて確認済みです)。**公開している C ABI は意図的に狭くしてあります** —— 面積を覆うことではなく、所有権・stride・エラー処理・IL2CPP・platform の振り分けを正しくすることを目的にしているからです。**Linux では v0.1.1 以降を使ってください** —— v0.1.0 の Linux プラグインは glibc 2.38 を要求し、Ubuntu 22.04 では読み込めませんでした。 ## これは何か @@ -18,7 +18,7 @@ OpenCV 5 を、このプロジェクトが所有する C ABI 越しに Unity へ ## やらないこと -OpenCV のアルゴリズムを実装し直すこと。OpenCV の API 全面を先回りして手で包むこと。互換のために OpenCvSharp の managed API を複製すること。あらゆる codec・DNN・GPU backend を有効にした 1 つの大きな binary を配ること —— `imgcodecs` はリンクしていますが PNG と JPEG だけで、TIFF / WebP / OpenEXR / JPEG 2000 はビルドから外してあります。動画入出力(FFmpeg、GStreamer)、DNN、GPU backend も同様です。 +OpenCV のアルゴリズムを実装し直すこと。OpenCV の API 全面を先回りして手で包むこと。互換のために OpenCvSharp の managed API を複製すること。あらゆる codec・GPU backend を有効にした 1 つの大きな binary を配ること —— `imgcodecs` はリンクしていますが PNG と JPEG だけで、TIFF / WebP / OpenEXR / JPEG 2000 はビルドから外してあります。動画入出力(FFmpeg、GStreamer)と GPU backend も同様です。**`dnn` だけは意図的な例外です** —— native binary には常に入っており、任意にできるのは C# 側です(下の「追加 profile: dnn」)。 ## 対応 platform @@ -41,7 +41,7 @@ encode / decode の検査が PNG ではなく JPEG を使うので、**画素の どちらの極端も通らないので、Web のビルドでは PNG を外してあります。 **他の 5 platform は PNG / JPEG の両方を扱えます。** -**PNG のデコードは Android・iOS・macOS でも ARM 加速を失っています**(Web だけではありません)。上流の OpenCV 5.0.0 の欠陥を回避するため、この 3 つの arm64 platform で `PNG_ARM_NEON` を切る必要があり、これはアセンブリで書かれたカーネルと NEON intrinsics の経路を両方とも落とします —— 同じビルドスイッチを共有しているためです。`dnn` を使うかどうかに関わらず、これらの platform で `CvCodecs` を使う利用者全員に影響します。理由の詳細は[ロードマップ](docs/roadmap.md)にあります。 +**PNG のデコードは Android・iOS・macOS でも ARM 加速を失っています**(Web だけではありません)。**これは v0.3.0 には入っておらず、次の版から効きます。** 上流の OpenCV 5.0.0 の欠陥を回避するため、この 3 つの arm64 platform で `PNG_ARM_NEON` を切る必要があり、これはアセンブリで書かれたカーネルと NEON intrinsics の経路を両方とも落とします —— 同じビルドスイッチを共有しているためです。`dnn` を使うかどうかに関わらず、これらの platform で `CvCodecs` を使う利用者全員に影響します。理由の詳細は[ロードマップ](docs/roadmap.md)にあります。 全体を通して Unity 6000.3 以降が必要です。 @@ -120,6 +120,9 @@ shasum -a 256 -c SHA256SUMS.txt # macOS ### 追加 profile: dnn +**v0.3.0 には入っていません。次の版から配られます。** v0.3.0 に対して +`OCVU_PROFILE_DNN` を立てても、その先には何もありません。 + native binary には OpenCV の `dnn` module(ONNX の読み込み、blob 化、forward 推論)が **常に**入っています —— profile ごとに別 binary へ分けてはいません。理由は、6 platform のうち iOS と WebGL の 2 つが `DllImport("__Internal")` を**シンボル名**で解決し、 @@ -195,6 +198,12 @@ Symbols** に `OCVU_PROFILE_DNN` を対象 platform 分足したときだけコ # Unity 同梱の Emscripten が tools/emscripten-versions.psd1 と一致することも確かめます。 ./tools/dev.ps1 test-unity-web +# GPU に依る RenderTexture の経路(EditMode、グラフィックス有効)と、 +# Player / graphics の 2 レーンから OCVU_BENCH: 行を集める benchmark。 +# **どちらもどの workflow からも走りません**(下の「CI が見ているもの」)。 +./tools/dev.ps1 test-unity-graphics +./tools/dev.ps1 benchmark + # UPM tarball を使い捨ての Unity プロジェクトへ導入し、そこでテストを走らせる。 # -PluginSource を渡さないとこのマシンの platform 分だけを固め、**そう述べます** # —— 全部入りのふりはしません。他 platform のプラグイン木(';' 区切り。 @@ -228,15 +237,17 @@ CI は Unity のレーン以外、すべて同じ `tools/dev.ps1` を呼びま モバイルの列が「クロスビルドのみ」なのは、それが CI にできることの全部だからです —— コンパイルして成果物を検査しますが、**どの実機もそれらを読み込んだことがありません。** -**この表に無いレーンが 2 つあります。どちらもどの workflow からも走りません。** `test-unity-web`(WebGL の Player を建ててブラウザで走らせる)は、CI 側が `Web browser E2E` job で同じことを別の形でやっています —— あちらは Player を自分で建て、`dev.ps1` を通さずに同じ 2 つのスクリプトを直接呼びます。もう 1 つは UPM tarball を使い捨てのプロジェクトへ導入するレーン(`test-unity-tarball`)で、ローカル専用で、上に書いた「導入できて通る」という結果は手で測ったものです。 +**この表に無いレーンが 4 つあります。どれもどの workflow からも走りません。** `test-unity-web`(WebGL の Player を建ててブラウザで走らせる)は、CI 側が `Web browser E2E` job で同じことを別の形でやっています —— あちらは Player を自分で建て、`dev.ps1` を通さずに同じ 2 つのスクリプトを直接呼びます。`test-unity-tarball`(UPM tarball を使い捨てのプロジェクトへ導入する)はローカル専用で、上に書いた「導入できて通る」という結果は手で測ったものです。残る 2 つ(`test-unity-graphics` と `benchmark`)は後から足したもので、**CI に相当するものが一切ありません。** + +**後ろの 2 つは、前の 2 つより重い意味を持ちます。他に同じところを見ているものが無いからです。** `RenderTextureConverter.ToMat` と `RequestMat` は、パッケージに入る公開 API ですが(**次の版から**。上の「現状」)、**実際に画素を運ぶ経路は CI で 1 度も実行されていません。** CI の Unity レーンは 2 つとも `-nographics` で走り、その下では `RenderTexture` の生成は成功するのに読み出した画素が塗った色になりません —— つまり GPU の経路はそこでは動かせず、それを動かす `test-unity-graphics` はローカル専用で merge を止めません。**引数の検証だけは CI が通っています**(`ToMat(null)` を拒むことは両レーンで実行されます)。通っていないのは GPU が要る側です。CI から見えないテストの一覧は `tests/UnityProject/Assets/Tests/EditMode/CiVisibilityTests.cs` が名指しで固定しているので、増えるときは意図して増やすことになります。 Unity のレーンは Linux で走り、Windows の IL2CPP Player はローカルのレーンだけが担います。**これはいまや推測ではなく実測に基づく結論です。** 以前ここに書いてあった理由は、実際には別の action と別のイメージ系統についての上流 issue を挙げていました。2026-08-31 に `windows-2022` で実際に試したところ、**EditMode は動いて 33 件通りました。Standalone は動きませんでした** —— IL2CPP は C++ を生成し、GameCI の Windows コンテナにはそれをコンパイルする MSVC が無いため、`ToolchainNotFoundException` でビルドが落ちます。**したがって Windows 固有の IL2CPP の欠陥は、見落としではなく設計上 CI に映りません。** macOS は同じ時期に 4 回試し、さらに手前で失敗します —— GameCI は darwin を支えず、エディタを直接入れる経路はライセンスが「entitlement 0 件」を返すところで止まります。ロードマップに両方の試行が記録されています。 **Linux の成果物は Ubuntu 22.04 のコンテナの中でビルドします**(runner のイメージ上ではありません)。共有ライブラリは、それをビルドした環境と同じかそれより新しいシステムでしか読み込めず、runner のイメージは前へ進み続けます。固定したコンテナでビルドすることで下限を意図した場所(glibc 2.35)に保ち、`tools/verify-plugin-portability.ps1` が、出てきたものがそれより新しいものを要求していればビルドを落とします。 -表の外では、すべての pull request が `actionlint` / `shellcheck` / `PSScriptAnalyzer` とリポジトリ内リンクの検査を走らせ、CodeQL が C++ と C# を解析します。nightly の workflow は Linux 成果物の glibc の下限を再確認し、Windows と macOS で速いレーンを走らせ、固定した OpenCV の artifact が期限切れでないことを確かめます —— **誰も push していない間に壊れるもの**です。**この nightly はまだ schedule で起動したことがありません**。手で 2 回起動しただけで、1 回目は失敗(API のレート制限)、2 回目は緑でした。 +表の外では、すべての pull request が `actionlint` / `shellcheck` / `PSScriptAnalyzer` とリポジトリ内リンクの検査を走らせ、CodeQL が C++ と C# を解析します。nightly の workflow は Linux 成果物の glibc の下限を再確認し、Windows と macOS で速いレーンを走らせ、固定した OpenCV の artifact が期限切れでないことを確かめます —— **誰も push していない間に壊れるもの**です。**この nightly は 2026-08-29 から毎日 schedule で走っています** —— 2026-09-10 時点で 13 回、直近 8 回は緑です。(以前ここには「まだ schedule で起動したことがない」と書いてありました。1 週間以上のあいだ偽で、**この種の記述は真でなくなっても何も赤くなりません。**) -**pull request で走るレーンのほとんどが merge を止めます。** 必須チェックは 21 本です: desktop 3 platform の契約・P/Invoke・sanitizer、Android と iOS のクロスビルド、lint の 4 job、CodeQL の 2 つ、Unity の 2 レーン、そしてその 5 platform 分の配布物をビルド・組み立てる release の 6 job。9 本は意図的に必須にしていません。うち 5 本は Unity のレーンが消費する platform ごとのプラグインをビルドするもので、**どれかが失敗すると Unity のレーンは走ったうえで材料が無いことで赤くなり**、それが merge を止めます。3 本は Web / Wasm のもの(クロスビルド・ブラウザでの E2E・リリースの梱包)で、**まだ昇格の実績を積んでいないため、現時点では Web のレーンが赤くても merge を止めません。****skip された必須チェックは合格として通る**ので、その守りなしにそれらへ依存すると、壊れたビルドが通ってしまいます。**レーンは安定して緑になってから必須にします** —— 過去 2 回、早すぎる昇格が「赤いのに merge できる」隙間を作りました。2026-08-29 までは Unity・lint・CodeQL の workflow がすべての pull request で走りながら必須ではなく、**赤いまま merge できました。CI が見ていることと CI が止めることは別で、ゲートなのは後者だけです。** 残る 3 つの workflow(`build-opencv`、`nightly`、`unity-probe`)は pull request では起動しないので、そもそも必須にできません。`release` は 2026-08-31 までその一覧にありました —— いまは pull request でも走ります。tag でしか走らなかった間に配布の経路に欠陥が 3 件たまり、**うち 1 件は「tag を打つとリリースが 1 件も作られない」というものでした。** その `Publish the release` job だけは必須にしません —— pull request では設計上 skip され、**skip は合格として通る**ので、必須にしても何も止まらないからです。 +**pull request で走るレーンのほとんどが merge を止めます。** 必須チェックは 21 本です: desktop 3 platform の契約・P/Invoke・sanitizer、Android と iOS のクロスビルド、lint の 4 job、CodeQL の 2 つ、Unity の 2 レーン、そしてその 5 platform 分の配布物をビルド・組み立てる release の 6 job。9 本は意図的に必須にしていません。うち 5 本は Unity のレーンが消費する platform ごとのプラグインをビルドするもので、**どれかが失敗すると Unity のレーンは走ったうえで材料が無いことで赤くなり**、それが merge を止めます。3 本は Web / Wasm のもの(クロスビルド・ブラウザでの E2E・リリースの梱包)で、**昇格していないため、現時点では Web のレーンが赤くても merge を止めません。** 以前ここには「まだ昇格の実績を積んでいない」と書いていましたが、2026-09-10 の時点でそれは成立しません —— 3 本とも直近 5 本の pull request すべてで緑で、そのうち main でも走る 2 本は main の直近 6 run でも緑です。**昇格するかどうかは、測定待ちではなく未決の判断です。****skip された必須チェックは合格として通る**ので、その守りなしにそれらへ依存すると、壊れたビルドが通ってしまいます。**レーンは安定して緑になってから必須にします** —— ただし過去 2 回踏んだのは、その逆の形でした: **レーンを足したのに必須にしないまま置いたこと**が、「赤いのに merge できる」隙間そのものです。2026-08-29 までは Unity・lint・CodeQL の workflow がすべての pull request で走りながら必須ではなく、**赤いまま merge できました。CI が見ていることと CI が止めることは別で、ゲートなのは後者だけです。** 残る 3 つの workflow(`build-opencv`、`nightly`、`unity-probe`)は pull request では起動しないので、そもそも必須にできません。`release` は 2026-08-31 までその一覧にありました —— いまは pull request でも走ります。tag でしか走らなかった間に配布の経路に欠陥が 3 件たまり、**うち 1 件は「tag を打つとリリースが 1 件も作られない」というものでした。** その `Publish the release` job だけは必須にしません —— pull request では設計上 skip され、**skip は合格として通る**ので、必須にしても何も止まらないからです。 ## 貢献とセキュリティ @@ -268,9 +279,14 @@ Windows では、実行時ライブラリを埋め込まず共有する形でリ - [M4 実装計画](docs/superpowers/plans/2026-08-30-m4-mobile.md) - [M5 実装計画](docs/superpowers/plans/2026-08-31-m5-binding-generator.md) - [M6 実装計画](docs/superpowers/plans/2026-09-03-m6-web-wasm.md) - -M5 の残りの条件を閉じた計画はそれらの隣にあります。全部の一覧は -[文書一覧](docs/README.md) にあります。 +- [M7 設計](docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md) —— 3 つの M7 計画が共有する仕様 +- [M7 (a) 低コピー経路と benchmark](docs/superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md) +- [M7 (b) module 分離](docs/superpowers/plans/2026-09-05-m7b-module-separation.md) +- [M7 (c) dnn profile](docs/superpowers/plans/2026-09-05-m7c-dnn-profile.md) + +M5 の残りの条件を閉じた計画と、M6 と M7 の間で API を広げた 6 本は、それらの +隣にあります。全部の一覧は [文書一覧](docs/README.md) にあります。 +- [性能](docs/performance.md) —— 測ったものと、意図して測っていないもの - [実機検証の手順](docs/m4-device-verification.md) —— CI では閉じないもの - [API リファレンス](docs/api-reference.md) - [API 対応表](docs/api-map.md) —— binding spec から生成 diff --git a/README.md b/README.md index ee12d88..227f4c4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ OpenCV 5 for Unity through a project-owned C ABI, distributed as a reproducible [日本語](README.ja.md) -> **Status: v0.3.0 is the newest published release (2026-09-04), and as this was written the repository and that release were in step** — what follows describes both. Whether the repository has since moved ahead is answered by the "配布" sections of the [roadmap](docs/roadmap.md), not here. It is the first release with six platforms — Windows x64, macOS arm64, Linux x64, Android arm64-v8a, iOS arm64 and Web/WebGL — the first with a generated binding layer, and the first with camera calibration. Every platform is built, tested and packaged by CI; Unity itself exercises the plugin on Mono (EditMode), a real IL2CPP player, and a headless browser. **Some honest limits.** **Android and iOS have never been run on a device** — CI cross-compiles them and inspects the artifacts, but no phone has loaded these binaries. **Web has no PNG**: `imgcodecs` there decodes and encodes JPEG only (see below), and the browser it was driven in is one headless Chromium on Linux. **Unity has never been run on macOS at all**, though its plugin settings are verified by asking Unity itself on other systems. The public C ABI is deliberately narrow, because the point was getting ownership, stride, error handling, IL2CPP and platform gating right rather than covering surface area. **If you are on Linux, take v0.1.1 or later:** the Linux plugin in v0.1.0 required glibc 2.38 and would not load on Ubuntu 22.04. +> **Status: v0.3.0 is the newest published release (2026-09-04), and the repository is now well ahead of it.** Everything below describes the repository, not that release. **v0.3.0 is tag `03a4557`, which predates both the September API expansion and all of M7** — so the gap is not a short list of features, it is everything merged since. The one number that is generated at both ends: `docs/api-map.md` lists **27 public C ABI functions at v0.3.0 and 57 here**. On the C# side `CvAruco`, `CvCoreOps`, `CvStereo`, `CvDnn` and `RenderTextureConverter` do not exist at that tag at all, so setting `OCVU_PROFILE_DNN` against v0.3.0 defines a symbol with nothing behind it. **What has actually shipped is recorded in the "配布" sections of the [roadmap](docs/roadmap.md)** — that, not this paragraph, is the source of truth for it. It is the first release with six platforms — Windows x64, macOS arm64, Linux x64, Android arm64-v8a, iOS arm64 and Web/WebGL — the first with a generated binding layer, and the first with camera calibration. Every platform is built, tested and packaged by CI; Unity itself exercises the plugin on Mono (EditMode), a real IL2CPP player, and a headless browser. **Some honest limits.** **Android and iOS have never been run on a device** — CI cross-compiles them and inspects the artifacts, but no phone has loaded these binaries. **Web has no PNG**: `imgcodecs` there decodes and encodes JPEG only (see below), and the browser it was driven in is one headless Chromium on Linux. **Unity has never been run on macOS at all**, though its plugin settings are verified by asking Unity itself on other systems. The public C ABI is deliberately narrow, because the point was getting ownership, stride, error handling, IL2CPP and platform gating right rather than covering surface area. **If you are on Linux, take v0.1.1 or later:** the Linux plugin in v0.1.0 required glibc 2.38 and would not load on Ubuntu 22.04. ## What this is @@ -18,7 +18,7 @@ A Unity-first integration of OpenCV 5, built around a narrow C ABI that this pro ## Non-goals -Reimplementing OpenCV algorithms. Hand-wrapping the entire OpenCV API up front. Duplicating OpenCvSharp's managed API for compatibility. Shipping one large binary with every codec, DNN and GPU backend enabled — `imgcodecs` is linked, but only with PNG and JPEG; TIFF, WebP, OpenEXR, JPEG 2000 and the rest are off in the build, as are video I/O (FFmpeg, GStreamer), DNN and every GPU backend. +Reimplementing OpenCV algorithms. Hand-wrapping the entire OpenCV API up front. Duplicating OpenCvSharp's managed API for compatibility. Shipping one large binary with every codec and GPU backend enabled — `imgcodecs` is linked, but only with PNG and JPEG; TIFF, WebP, OpenEXR, JPEG 2000 and the rest are off in the build, as are video I/O (FFmpeg, GStreamer) and every GPU backend. **`dnn` is the exception, and it is deliberate**: it is always in the native binary, and what you opt into is the C# side (see *Optional profiles: dnn* below). ## Platforms @@ -55,6 +55,7 @@ fail to link on undefined ones. Neither extreme works, so the Web build has PNG off. Every other platform has both. **PNG decoding lost its ARM acceleration on Android, iOS and macOS** (not just Web). +**This is not in v0.3.0; it takes effect from the next release.** Working around an upstream OpenCV 5.0.0 defect required turning off `PNG_ARM_NEON` on those three arm64 platforms, which drops both the hand-written assembly kernel and the NEON intrinsics path — they share the same build switch. This affects every `CvCodecs` @@ -188,6 +189,9 @@ every release, so they are not repeated here. ### Optional profiles: dnn +**Not in v0.3.0 — this ships in the next release.** Setting `OCVU_PROFILE_DNN` +against v0.3.0 defines a symbol with nothing behind it. + The native binary always contains OpenCV's `dnn` module (ONNX loading, blob conversion, and forward inference) — it is not split into a separate binary, because two of the six platforms (iOS, WebGL) resolve `DllImport("__Internal")` @@ -282,6 +286,12 @@ All local development after that goes through `tools/dev.ps1`: # Also asserts that Unity's bundled Emscripten matches tools/emscripten-versions.psd1. ./tools/dev.ps1 test-unity-web +# The GPU-dependent RenderTexture paths (EditMode, graphics enabled) and the +# benchmark lane that collects OCVU_BENCH: lines from the player and graphics +# lanes. Neither runs in any workflow — see "What CI covers" below. +./tools/dev.ps1 test-unity-graphics +./tools/dev.ps1 benchmark + # Install the UPM tarball into a throwaway Unity project and run its tests there. # Without -PluginSource this packs only this machine's own platform, and says so # rather than pretending to be the all-platform package. Pass the other platforms' @@ -320,12 +330,26 @@ different: CI builds a real WebGL player and **runs it in a headless Chromium**, same checks EditMode and the IL2CPP player run also run in a browser. There is no sanitizer lane for Web (a cross-compiled sanitizer cannot run on the host). -Two lanes are absent from that table because they run in no workflow at all. +Four lanes are absent from that table because they run in no workflow at all. `test-unity-tarball` installs the UPM tarball into a throwaway project; it is local only, and the "installs and passes" result above was measured by hand. `test-unity-web` builds the WebGL player and drives it in a browser; CI covers the same ground in its own `Web browser E2E` job, which builds the player itself and calls the same two scripts directly rather than going through `dev.ps1`. +`test-unity-graphics` and `benchmark` were added later and have no CI counterpart +at all. + +**That last pair matters more than the first two, because nothing else covers the +same ground.** `RenderTextureConverter.ToMat` and `RequestMat` are public API in +the package (from the next release — see *Status*), and **the code path that +actually moves pixels has never run in CI.** Both CI Unity lanes run with `-nographics`, where `RenderTexture` +creation succeeds but the pixels read back are not the ones you drew — so the +GPU path cannot be exercised there, and `test-unity-graphics`, which does exercise +it, is local-only and does not block a merge. Argument validation *is* covered +(`ToMat(null)` is rejected in both CI lanes); it is the GPU half that is not. +`tests/UnityProject/Assets/Tests/EditMode/CiVisibilityTests.cs` pins the list of +tests CI cannot see, so the list has to be edited deliberately rather than growing +by accident. The Unity lanes run on Linux, and the Windows IL2CPP player is covered only by the local lane. **That is now a measured conclusion rather than an assumption.** An earlier @@ -349,9 +373,11 @@ Beyond the table, every pull request also runs `actionlint`, `shellcheck`, `PSScriptAnalyzer` and a repository-relative link check, and CodeQL analyses the C++ and C#. A nightly workflow re-checks the Linux artifact's glibc floor, runs the fast lanes on Windows and macOS, and confirms the pinned OpenCV artifacts have not -expired — things that break while nobody is pushing. **The nightly workflow has not -yet run on its schedule**; it has only been started by hand, once unsuccessfully -(API rate limits) and once green. +expired — things that break while nobody is pushing. **It has run on its schedule +every day since 2026-08-29** — thirteen scheduled runs as of 2026-09-10, the last +eight of them green. (An earlier version of this paragraph said it had never run on +a schedule. That was wrong for over a week, and nothing goes red when a statement +like that stops being true.) **Almost every lane that runs on a pull request blocks a merge.** Twenty-one checks are required: the contract, P/Invoke and sanitizer jobs across the three desktop platforms, the @@ -360,11 +386,15 @@ and the six release jobs that build and assemble the distributable for those fiv Nine are deliberately not required. Five build the per-platform plugins the Unity lanes consume: when one fails the Unity lanes run anyway and go red on the missing input, which is what stops the merge. Three cover Web/Wasm — its cross-build, its browser end-to-end -test and its release packaging — and are new enough that they have not yet earned -promotion, so **a red Web lane does not currently stop a merge**. A skipped required check counts as passing, so depending on one -without that guard would let a broken build through. A lane is only made required once it -has been reliably green — twice before, promoting a lane too early left a gap where a red -check could not stop a merge. Until 2026-08-29 the +test and its release packaging — and **have not been promoted, so a red Web lane does +not currently stop a merge**. The stated reason used to be that they were too new; as of +2026-09-10 that no longer holds — all three have passed on five consecutive pull +requests, and the two that also run on `main` have passed on its last six runs. +**Promotion is an open decision, not a pending measurement.** A skipped required check +counts as passing, so depending on one without that guard would let a broken build +through. A lane is only made required once it has been reliably green — but the failure +mode seen twice here was the opposite of impatience: **a lane was added and left +un-required**, which is exactly the gap where a red check cannot stop a merge. Until 2026-08-29 the Unity, lint and CodeQL workflows ran on every pull request without being required, so they could be red and the change still merged; CI watching something and CI stopping something are different things, and only the second one is a gate. The @@ -436,9 +466,15 @@ Design and research documents (in Japanese) live under `docs/`: - [M4 implementation plan](docs/superpowers/plans/2026-08-30-m4-mobile.md) - [M5 implementation plan](docs/superpowers/plans/2026-08-31-m5-binding-generator.md) - [M6 implementation plan](docs/superpowers/plans/2026-09-03-m6-web-wasm.md) +- [M7 design](docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md) — the shared spec for the three M7 plans +- [M7 (a) low-copy paths and benchmarks](docs/superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md) +- [M7 (b) module separation](docs/superpowers/plans/2026-09-05-m7b-module-separation.md) +- [M7 (c) dnn profile](docs/superpowers/plans/2026-09-05-m7c-dnn-profile.md) -The plans that closed M5's remaining criterion live beside them; the +The plans that closed M5's remaining criterion, and the six that expanded the API +between M6 and M7, live beside them; the [documentation index](docs/README.md) lists all of them. +- [Performance](docs/performance.md) — what was measured, and what deliberately was not - [Device verification checklist](docs/m4-device-verification.md) — what CI cannot close - [API reference](docs/api-reference.md) - [API map](docs/api-map.md) — generated from the binding spec diff --git a/docs/README.md b/docs/README.md index a6db491..6eb6581 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,12 +9,12 @@ - M0〜M7(2026-08-29 に足した **M3.5** を含む)の各マイルストーンの目的・ゴール・完了条件・非ゴール - **差別化の穴**(2026-08-29 の再調査。競合が全員 OpenCV 4 系のままであること、差別化の穴 10 件(掲げている差別化のうち達成していないものと、競合が持っていて本案が持たないものの合算)と、その担当。**解消・部分達成の別は roadmap の表が持つ** —— 判定は今後も動くので、件数をここに写さない) - **M7 の上流調査**(2026-08-30。OpenCV 5.1 に向けて `dnn` がどう動いているかを一次情報で確かめ、`dnn` を足す前に native bridge を module 単位に分ける決定を入れた) - - ローカルループと CI の役割分担、GitHub Actions のワークフロー構成(M3 の後に `ci-lint` / `codeql` / `nightly` と Dependabot を追加。**`nightly` は schedule でまだ 1 度も走っていない**) - - 実装計画: [M0 自動 TDD ハーネス](./superpowers/plans/2026-08-25-m0-tdd-harness.md)(完了)、[M1 OpenCV ビルド](./superpowers/plans/2026-08-25-m1-opencv-build.md)(完了)、[M2 Windows vertical slice](./superpowers/plans/2026-08-26-m2-windows-vertical-slice.md)(完了。8 件中 8 件。条件 7 は game-ci + Linux で満たした — 詳細は roadmap の M2 節)、[M3 Desktop 3 platform と配布の再現性](./superpowers/plans/2026-08-28-m3-desktop-three-platforms.md)(6 件すべて達成。v0.1.0 と v0.1.1 を公開済み——詳細は roadmap の M3 節)、[M3.5 配布の形と最小の穴](./superpowers/plans/2026-08-30-m3.5-distribution-shape.md)(全部入り tarball / `imgcodecs` / Unity 6.3。**6 件すべて達成。v0.2.0 を公開し OpenUPM にも登録済み**——詳細は roadmap の M3.5 節)、[M4 Mobile](./superpowers/plans/2026-08-30-m4-mobile.md)(Android / iOS のクロスビルドと `WebCamTexture` 対応。**9 件中 6 件を満たし、3 件は閉じていない** —— iOS 実機 / lifecycle / macOS 上の Unity(**Windows IL2CPP は「結論を出す」ことが条件だったので満たしている**)。実機が要る 2 件は [実機検証の手順](./m4-device-verification.md) に落としてある)、[M5 binding specification と generator](./superpowers/plans/2026-08-31-m5-binding-generator.md)(spec を正本にして C ヘッダ・C# の P/Invoke・到達性テスト・[API 対応表](./api-map.md) を生成する。**5 件中 4 件を満たし、条件 2 は計画の側で意図的に外してある** —— 詳細は roadmap の M5 節)、[M5 条件 2 の前半 objdetect / features](./superpowers/plans/2026-09-01-m5-modules-objdetect-features.md)(QR コードの符号化・復号と ORB の特徴点検出を出し、条件 2 を「部分的に満たした」にした。続けて `geometry`(射影変換の推定)も出した)、[M5 条件 2 の最後 カメラの歪み補正](./superpowers/plans/2026-09-01-m5-calib-undistort.md)(`ocvu_undistort` / `ocvu_find_chessboard_corners` を `calib` module を足さずに出した。**この時点では係数を求める `cv::calibrateCamera` が無く、条件 2 は「部分的に満たした」のままだった**)、[M5 条件 2 の完了 カメラ校正](./superpowers/plans/2026-09-02-m5-calib-camera.md)(**`calib` module を足し、`cv::calibrateCamera` を出して校正の輪を閉じた。構成ハッシュが変わり 5 platform 分の OpenCV を作り直した**(`4785d98e9aad` → `09fcbe260d87`)—— 詳細は roadmap の M5 節)、[M6 Web / Wasm](./superpowers/plans/2026-09-03-m6-web-wasm.md)(**6 つ目の platform。クロスビルドかつ静的ライブラリ**で、**ブラウザで実際に走らせて初めて出る欠陥を 7 件捕まえた** —— そのうち「例外バリアが wasm で成立していなかった」が最も重い。**完了条件 5 件すべてを満たした** —— 詳細は roadmap の M6 節)、**[API 拡張(A〜F)—— 実測で覆った前提と、その決定](./superpowers/plans/2026-09-05-api-expansion-corrections.md)(下の 5 本すべてに優先する。実装前に 12 観点 x 2 段で計画の前提を実測し、ブロッカー 11 件・要修正 22 件・見落とし 53 件を潰した記録。**反証段が調査段の修正案そのものを 2 件否定している**)**、[API 拡張(A〜F)の全体設計](./superpowers/plans/2026-09-05-api-surface-expansion.md)(**マイルストーンではない。M6 と M7 の間に挟む API 拡張**で、既にリンク済みの module から 26 本を出す。**OpenCV の再ビルドは 1 度も起こさない**。下の 4 つの計画がこれを分担する)、[Phase 1 姿勢と ArUco](./superpowers/plans/2026-09-05-api-pose-and-aruco.md)、[Phase 2 imgproc の実用関数](./superpowers/plans/2026-09-05-api-imgproc-ops.md)、[Phase 3 core の基本演算](./superpowers/plans/2026-09-05-api-core-ops.md)、[Phase 4 マッチングとステレオ](./superpowers/plans/2026-09-05-api-matching-and-stereo.md)(**`stereo` を 8 つ目のリンク済み module にする唯一の計画**)、**[M7 Optional profiles と性能の設計](./superpowers/plans/2026-09-05-m7-profiles-and-performance.md)(**仕様であって計画ではない。**下の 3 本が共有する正本で、完了条件の担当・Global Constraints・7 つの決定を持つ)**、[M7 (a) 低コピー経路と benchmark](./superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md)(**決定的な量は assert し、時間は公開するだけにする。**RenderTexture / AsyncGPUReadback を出し、native texture pointer は「やらないと決めた」として評価だけを記録する。**公開 ABI を 1 本も増やさない**)、[M7 (b) module 分離](./superpowers/plans/2026-09-05-m7b-module-separation.md)(**(c) の前提。**native の CMake と C# の assembly を module 単位に割る。**先に安全網を作る** —— 配布 binary のエクスポートが spec と完全一致することを要求する検査を足してから作り替える。**公開 ABI を 1 本も増やさない**)、[M7 (c) dnn profile](./superpowers/plans/2026-09-05-m7c-dnn-profile.md)(**着手してよいという合図ではない** —— roadmap は「5.0 に固定した DNN ラッパーを作り込まない」と決めており、これは決定が覆ったときに何をするかを書いたものである。**(b) が済むまで着手しない**) + - ローカルループと CI の役割分担、GitHub Actions のワークフロー構成(M3 の後に `ci-lint` / `codeql` / `nightly` と Dependabot を追加。**`nightly` は 2026-08-29 から schedule で毎日走っている** —— **「まだ 1 度も走っていない」と書いてあったのは逆向きの陳腐化で、動いている検査を存在しないことにしていた**。実績は roadmap のワークフロー表) + - 実装計画: [M0 自動 TDD ハーネス](./superpowers/plans/2026-08-25-m0-tdd-harness.md)(完了)、[M1 OpenCV ビルド](./superpowers/plans/2026-08-25-m1-opencv-build.md)(完了)、[M2 Windows vertical slice](./superpowers/plans/2026-08-26-m2-windows-vertical-slice.md)(完了。8 件中 8 件。条件 7 は game-ci + Linux で満たした — 詳細は roadmap の M2 節)、[M3 Desktop 3 platform と配布の再現性](./superpowers/plans/2026-08-28-m3-desktop-three-platforms.md)(6 件すべて達成。v0.1.0 と v0.1.1 を公開済み——詳細は roadmap の M3 節)、[M3.5 配布の形と最小の穴](./superpowers/plans/2026-08-30-m3.5-distribution-shape.md)(全部入り tarball / `imgcodecs` / Unity 6.3。**6 件すべて達成。v0.2.0 を公開し OpenUPM にも登録済み**——詳細は roadmap の M3.5 節)、[M4 Mobile](./superpowers/plans/2026-08-30-m4-mobile.md)(Android / iOS のクロスビルドと `WebCamTexture` 対応。**9 件中 6 件を満たし、3 件は閉じていない** —— iOS 実機 / lifecycle / macOS 上の Unity(**Windows IL2CPP は「結論を出す」ことが条件だったので満たしている**)。実機が要る 2 件は [実機検証の手順](./m4-device-verification.md) に落としてある)、[M5 binding specification と generator](./superpowers/plans/2026-08-31-m5-binding-generator.md)(spec を正本にして C ヘッダ・C# の P/Invoke・到達性テスト・[API 対応表](./api-map.md) を生成する。**5 件中 4 件を満たし、条件 2 は計画の側で意図的に外してある** —— 詳細は roadmap の M5 節)、[M5 条件 2 の前半 objdetect / features](./superpowers/plans/2026-09-01-m5-modules-objdetect-features.md)(QR コードの符号化・復号と ORB の特徴点検出を出し、条件 2 を「部分的に満たした」にした。続けて `geometry`(射影変換の推定)も出した)、[M5 条件 2 の最後 カメラの歪み補正](./superpowers/plans/2026-09-01-m5-calib-undistort.md)(`ocvu_undistort` / `ocvu_find_chessboard_corners` を `calib` module を足さずに出した。**この時点では係数を求める `cv::calibrateCamera` が無く、条件 2 は「部分的に満たした」のままだった**)、[M5 条件 2 の完了 カメラ校正](./superpowers/plans/2026-09-02-m5-calib-camera.md)(**`calib` module を足し、`cv::calibrateCamera` を出して校正の輪を閉じた。構成ハッシュが変わり 5 platform 分の OpenCV を作り直した**(`4785d98e9aad` → `09fcbe260d87`)—— 詳細は roadmap の M5 節)、[M6 Web / Wasm](./superpowers/plans/2026-09-03-m6-web-wasm.md)(**6 つ目の platform。クロスビルドかつ静的ライブラリ**で、**ブラウザで実際に走らせて初めて出る欠陥を 7 件捕まえた** —— そのうち「例外バリアが wasm で成立していなかった」が最も重い。**完了条件 5 件すべてを満たした** —— 詳細は roadmap の M6 節)、**[API 拡張(A〜F)—— 実測で覆った前提と、その決定](./superpowers/plans/2026-09-05-api-expansion-corrections.md)(下の 5 本すべてに優先する。実装前に 12 観点 x 2 段で計画の前提を実測し、ブロッカー 11 件・要修正 22 件・見落とし 53 件を潰した記録。**反証段が調査段の修正案そのものを 2 件否定している**)**、[API 拡張(A〜F)の全体設計](./superpowers/plans/2026-09-05-api-surface-expansion.md)(**マイルストーンではない。M6 と M7 の間に挟む API 拡張**で、既にリンク済みの module から 26 本を出す。**OpenCV の再ビルドは 1 度も起こさない**。下の 4 つの計画がこれを分担する)、[Phase 1 姿勢と ArUco](./superpowers/plans/2026-09-05-api-pose-and-aruco.md)、[Phase 2 imgproc の実用関数](./superpowers/plans/2026-09-05-api-imgproc-ops.md)、[Phase 3 core の基本演算](./superpowers/plans/2026-09-05-api-core-ops.md)、[Phase 4 マッチングとステレオ](./superpowers/plans/2026-09-05-api-matching-and-stereo.md)(**`stereo` を 8 つ目のリンク済み module にする唯一の計画**)、**[M7 Optional profiles と性能の設計](./superpowers/plans/2026-09-05-m7-profiles-and-performance.md)(**仕様であって計画ではない。**下の 3 本が共有する正本で、完了条件の担当・Global Constraints・7 つの決定を持つ。**3 本とも実行済みなので、着手前の記録として読むこと**)**、[M7 (a) 低コピー経路と benchmark](./superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md)(**実行済み(`be5615f`)。完了条件 2・3 を満たした。決定的な量は assert し、時間は公開するだけにする。**RenderTexture / AsyncGPUReadback を出し、native texture pointer は「やらないと決めた」として評価だけを記録する。**公開 ABI を 1 本も増やさない**)、[M7 (b) module 分離](./superpowers/plans/2026-09-05-m7b-module-separation.md)(**実行済み(`be5615f`)。完了条件 4 を満たした。**native の CMake と C# の assembly を module 単位に割る。**先に安全網を作る** —— 配布 binary のエクスポートが spec と完全一致することを要求する検査を足してから作り替える。**公開 ABI を 1 本も増やさない**)、[M7 (c) dnn profile](./superpowers/plans/2026-09-05-m7c-dnn-profile.md)(**実行済み(2026-09-08〜10、`e1b0930`)。完了条件 1 を満たした。** `dnn` の C ABI 4 本と `CvUnity.Dnn` を opt-in profile として出した。**「着手してよいという合図ではない」という但し書きは 2026-09-08 の gate lift で失効した** —— 経緯は roadmap の「この決定は解除された」) - [API リファレンス](./api-reference.md) - allowlist に載っている C ABI 関数(M2 の `Mat` のライフサイクルと buffer 転送 + `cvtColor` / `resize` / `GaussianBlur`、**M3.5 の `imencode` / `imdecode`**、**2026-09 の API 拡張で姿勢 / ArUco / imgproc の実用関数 / core の基本演算 / 記述子マッチング / ステレオ視差の 26 本**、**M5 の `qr_encode` / `qr_decode` / `orb_detect` / `find_homography` / `undistort` / `find_chessboard_corners` / `calibrate_camera`**)と、その上に立つ C# の公開 API - - **allowlist の本数は公開 ABI の総数ではありません。** 残りは M0 / M1 由来の診断用 API(ABI version の取得、last-error の取得、status 表の照会、OpenCV version / build information)と `ocvu_debug_crash` です。**どちらの数も [API 対応表](./api-map.md) の冒頭と [所有権と versioning](./abi-ownership-and-versioning.md) §3 が持ちます** —— ここには写しません。 - - **まだ無い機能は書きません。** 範囲は M2 で確定し、M3.5 の `imgcodecs` と M5 の module 拡張が加わった allowlist に一致します + - **allowlist の本数は公開 ABI の総数ではありません。** 残りは M0 / M1 由来の診断用 API(ABI version の取得、last-error の取得、status 表の照会、OpenCV version / build information)と `ocvu_debug_throw` / `ocvu_debug_crash` です(**一覧の正本は [API リファレンス](./api-reference.md) の「この allowlist に含まれないもの」です** —— ここに写すと 1 本増えた日にこの行だけが嘘になります。**実際 `ocvu_debug_throw` が抜けていました**)。**どちらの数も [API 対応表](./api-map.md) の冒頭と [所有権と versioning](./abi-ownership-and-versioning.md) §3 が持ちます** —— ここには写しません。 + - **まだ無い機能は書きません。** 範囲は M2 で確定し、M3.5 の `imgcodecs`、M5 の module 拡張、2026-09 の API 拡張(姿勢 / ArUco / imgproc / core / マッチング / ステレオ)、M7a の `RenderTexture`、**M7c の `dnn`(opt-in profile)**が加わった allowlist に一致します - [API 対応表](./api-map.md) - **生成物です。** `bindings/spec/*.json` が正本で、`./tools/dev.ps1 generate` が書き出します。手で編集すると `verify-generated` が赤くなります - spec に載っている entry を 1 行ずつ並べます。**本数は表の冒頭が数えます** —— C ABI と C# の P/Invoke 宣言は別に数えており、差は同じ C の entry point へ別の引数の形で入る C# 側の入口です(C ABI を増やしません)。**ここに数字を書かないのは、ABI が 1 本増えるとこの行だけが静かに嘘になるからです** @@ -56,7 +56,7 @@ ## Status -M0〜M3.5 は完了しました(**M3.5 は完了条件 6 件すべてを 2026-08-30 に満たしました**。判定は roadmap の M3.5 節が正本)。M2(Windows vertical slice)は 8 件、M3(Desktop 3 platform と配布の再現性)は 6 件の完了条件をすべて実測で満たし、**v0.1.0(2026-08-28)と v0.1.1(2026-08-29)を公開しました**(**その後 v0.2.0 / v0.3.0 と重ね、現在の最新は v0.3.0(2026-09-04、6 platform)です**)。**M3.5 の成果は v0.2.0 で、M4 / M5 / M6 の成果は v0.3.0 で配りました** —— 次の版で初めて利用者に届きます。 +M0〜M3.5 は完了しました(**M3.5 は完了条件 6 件すべてを 2026-08-30 に満たしました**。判定は roadmap の M3.5 節が正本)。M2(Windows vertical slice)は 8 件、M3(Desktop 3 platform と配布の再現性)は 6 件の完了条件をすべて実測で満たし、**v0.1.0(2026-08-28)と v0.1.1(2026-08-29)を公開しました**(**その後 v0.2.0 / v0.3.0 と重ね、現在の最新は v0.3.0(2026-09-04、6 platform)です**)。**M3.5 の成果は v0.2.0 で、M4 / M5 / M6 の成果は v0.3.0 で配りました。M7 の成果はまだ配っていません** —— 最新の公開版は v0.3.0 で、`dnn` を含む M7 の成果が利用者に届くのは次の版からです。 M2 の最後に残っていた条件 7(CI 上で L4/L5 を実行)は、game-ci でランナーへの Unity 導入とライセンスのアクティベーションを行い、Linux で走らせることで満たしました。**その過程で、公開済み v0.1.0 の Linux 版が古い環境で読み込めない欠陥が判明しました** —— ubuntu-24.04 でビルドした `.so` が GLIBC_2.38 を要求していたためで、Unity を CI で実際に動かすまで誰も気づけませんでした。Linux のビルドを `ubuntu:22.04` コンテナへ移し、要求を 2.34 に下げたうえで、ビルド時点で上限を検査するようにしています。 @@ -103,4 +103,26 @@ M6 節が持ちます)。**ただし動かしているブラウザはそれ 1 破棄して打ち直しました。配布の記録は [ロードマップ](./roadmap.md) の「配布 その 5」にあります。 +M7(Optional profiles と性能)は**完了条件 5 件すべてを満たしました**(2026-09-10)。 +**M7 が最後のマイルストーンで、後続はありません** —— roadmap の `## M` 見出しは +M0 から M7 までです。**判定が 1 節にまとまっていないのは M7 だけ**で、5 件が +`M7a の判定` / `M7b の判定` / `CUDA / cuDNN 同梱の判定` / `M7c の判定` の 4 節に +分かれています(索引は roadmap の `M7 の判定`)。出したのは、低コピー経路の実測と +benchmark の公開(`RenderTexture` / `AsyncGPUReadback`)、C ABI と C# の module 分離、 +CUDA / cuDNN を同梱しないという決定、そして **`dnn` を opt-in profile として出すこと** +(`OCVU_PROFILE_DNN` を立てたときだけコンパイルされる C# の `CvUnity.Dnn`。 +native binary には既定で入っています)です。 + +**満たしたことと、実証したことは同じではありません。** M7 が残した穴を、 +利用者に関わるものだけ挙げます —— **`dnn` は実機で 1 度も動かしておらず、推論の +速さも測っていません**。`ocvu_dnn_net_forward` の `.clone()` が要ることを示す +再現テストがなく、**確定させる経路がこのリポジトリの構成上ありません**。 +`dnn` が引き込む MLAS / ONNX Runtime は `libopencv_dnn.a` に静的に取り込まれるため +**依存 allowlist からは見えません**。`WITH_CAROTENE` / `WITH_KLEIDICV` は既定 ON の +まま監査していません。**third-party のライセンス集合は platform ごとに実際に違う +のに、`THIRD_PARTY_NOTICES.md` は 1 通で全 platform を代表しています。** +`RenderTexture` の画素を運ぶ経路は CI で 1 度も実行されません +(`test-unity-graphics` はローカル専用です)。詳細は roadmap の `M7 の判定` と、 +各判定節の「穴を隠さず書く」にあります。 + 本文中の「推奨」「目標」「案」のうち、まだ実装されていない部分は依然として設計提案であり、実装済み機能や動作確認結果ではありません。両者の区別は各文書内の記述を見て判断してください。競合製品のバージョンや対応状況は変わるため、実装開始時と公開前に再確認します。 diff --git a/docs/abi-ownership-and-versioning.md b/docs/abi-ownership-and-versioning.md index c126bf5..03f8ea2 100644 --- a/docs/abi-ownership-and-versioning.md +++ b/docs/abi-ownership-and-versioning.md @@ -940,8 +940,10 @@ Haar / HOG(**OpenCV 5 で contrib へ移った**ので、この構成では出 「どの宣言がコンパイルされるか」であって、「境界の契約が何版か」ではない。** **native 側の実体は `native/modules.cmake` である。** `OCVU_MODULES` に -渡す module の一覧(既定は `OCVU_ALL_MODULES` の全 9 module)が、その -binary に入る関数を決める。**外せない module が 2 つある**(`infra` と +渡す module の一覧(既定は同ファイルの `OCVU_ALL_MODULES` の全部)が、その +binary に入る関数を決める。**本数をここに写さない。正本は `OCVU_ALL_MODULES` +そのものである** —— **写していた頃は実際に古くなった**(M7c で `dnn` が加わった +のに「全 9 module」と書いたままだった)。**外せない module が 2 つある**(`infra` と `core`)—— 他の全 module が使うので、外そうとすると configure の時点で `FATAL_ERROR` になる(`OCVU_REQUIRED_MODULES`)。 diff --git a/docs/m4-device-verification.md b/docs/m4-device-verification.md index f2c4f9d..33b3fbf 100644 --- a/docs/m4-device-verification.md +++ b/docs/m4-device-verification.md @@ -18,8 +18,14 @@ roadmap の M4 完了条件のうち、次の 2 件がこれに当たる。 配る前提条件ではなくなった** —— 配ったあとで誰かが実施して、動くかどうかを 確かめるためのものである。 -**`v0.3.0` の下書きを公開してはならない。** M5 が main に入る前に作ったもので、 -生成物が 1 つも入っていない。配るときは tag を打ち直す(roadmap の「配布 その 5」)。 +**~~`v0.3.0` の下書きを公開してはならない。~~ この警告は 2026-09-04 に失効した。** +指していた下書きは M5 が main に入る前に作ったもので生成物が 1 つも入っておらず、 +**下書きと tag は破棄して `03a4557` で打ち直し、v0.3.0 として公開済みである** +(roadmap の「配布 その 5」)。**いまの v0.3.0 は「出してはいけないもの」ではなく、 +現在の最新公開版である。** + +**この手順書は `dnn`(M7c)を扱っていない。** M7 で `dnn` が opt-in profile として +入ったが、実機での確認手順はここに無い —— **`dnn` も実機で 1 度も動かしていない。** **ここを実施して問題が見つかったら、その版を直した新しい版を出す**(一度配った ものは黙って差し替えない。v0.1.1 がその形だった)。 diff --git a/docs/performance.md b/docs/performance.md index f5c3744..f213238 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -172,3 +172,9 @@ backend の話になり、M7 の CUDA に関する決定(同梱しない)に 可能性が高く、真の初回ロード時間ではない - **IL2CPP Player での `RenderTexture` の経路。** `RenderTextureConverter.ToMat` / `RequestMat` は Editor(Mono)の `test-unity-graphics` でしか実行されたことがない +- **`dnn` の推論(M7c、2026-09-10)。** `./tools/dev.ps1 benchmark` の対象に `dnn` の + 項目は無く、`OCVU_BENCH:` 行を `ocvu_dnn_net_forward` から出したことも無い。 + **モデルの読み込み・blob 化・forward のいずれについても、このリポジトリは + 数字を 1 つも持っていない** —— 実機で動かしたことが無いこと(上の 1 つ目)と + 合わせて、`dnn` は「作ってあるが、速さは分からない」状態である + (roadmap の `### M7c の判定`) diff --git a/docs/roadmap.md b/docs/roadmap.md index 6549e3f..83aeb7b 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -75,7 +75,7 @@ public OSS リポジトリのため GitHub-hosted runner を無償で使える | `release.yml` | tag / **pull request**(空撃ち) / `workflow_dispatch` | **全部入りの UPM tarball(配る正)** と platform ごとの tarball、manifest / checksums / SBOM / third-party notices と `SHA256SUMS.txt` を GitHub Release へ。**staging した数を数え**、全部入りが名前で並んでいることも見る(**件数は platform が増えれば増えるので、実数は `CLAUDE.md` の workflow 表が持つ**)。**pull request でも走るようにしたのは M4 の後**で、tag でしか走らなかった間に欠陥が 3 件たまったためである(うち 1 件は「tag を打つと Release が 1 件も作られない」)。**全部入りには SBOM と build-manifest を付けない** —— どちらも復元済みの OpenCV artifact から作るので、束ねる job には元が無く、混ぜた版を捏造しない。**M3.5 が足した配線(全部入りの組み立て・17 件の staging・SHA256SUMS)は 2026-08-30 の空撃ちで初めて通した。** それまで空撃ちは publish job を丸ごと飛ばしており、**束ねる側は tag を打つまで 1 行も動かなかった** —— job を `assemble`(条件なし)と `publish`(job 単位で tag に限る)に割って直した。**実績は 2 つ**: run 33286928144 は条件を最後の step に降ろしただけの形(レビューで取り消した)、run 33289128197 が**いまの 2 job 構成**である。どちらも Release は作られていない。**tag で 2 回実行済み**(v0.1.0 = 2026-08-28、v0.1.1 = 2026-08-29。どちらも `--draft` で下書きを作り、人が点検してから公開した)。**M3 当時の空撃ち**(run 33156465235、3 platform とも success)は publish job ごと skip されていた —— **この形は 2026-08-30 に変えた**(上記)ので、いまの空撃ちは Release を作る job 以外を通る | M3 | | `ci-lint.yml` | push(main) / PR / 手動 | actionlint / shellcheck / PSScriptAnalyzer / 文書の相対リンク検査の 4 job。**静的に読めば分かる誤りを、CI を 1 周(10〜20 分)回して確かめていた**のを埋める | M3 後 | | `codeql.yml` | push(main) / PR / 週 1 / 手動 | C++ と C# の静的解析。sanitizer が「実際に踏んだ経路」を見るのに対し、CodeQL は経路を実行せずに探すので**重なっていない** | M3 後 | -| `nightly.yml` | 毎日 04:00 UTC / 手動 | 誰も push していない間に壊れることを見つける。Linux 成果物の移植性 / Windows・macOS の速いレーン / OpenCV artifact の期限切れ確認の **3 job 定義**(速いレーンは `lanes` という 2 runner の matrix なので、**実行時は 4 件**になる)。**schedule での実行実績はまだ無い**(下記) | M3 後 | +| `nightly.yml` | 毎日 04:00 UTC / 手動 | 誰も push していない間に壊れることを見つける。Linux 成果物の移植性 / Windows・macOS の速いレーン / OpenCV artifact の期限切れ確認の **3 job 定義**(速いレーンは `lanes` という 2 runner の matrix なので、**実行時は 4 件**になる)。**2026-08-29 から schedule で毎日走っている**(下記) | M3 後 | **M3 の後に足したもの**(マイルストーンの完了条件ではなく、CI/CD の監査で出た穴を塞ぐもの) @@ -84,15 +84,27 @@ public OSS リポジトリのため GitHub-hosted runner を無償で使える - `.github/dependabot.yml` — `actions/*` と `tests/Managed` の NuGet を週 1 で追う。可変タグで固定しているので、**上流が変われば何もしていないのに壊れる**。その変化を差分として見える形にする - `SECURITY.md` / `CONTRIBUTING.md` — OSS として欠けていた。前者は非公開の脆弱性報告先とこの境界で何が範囲内か、後者は貢献の手順と**CI が見ないもの**を明示する -**`nightly.yml` は schedule でまだ 1 度も走っていない。** 手動起動が 2 回あるだけで、 +**`nightly.yml` は schedule で毎日走っている。** 最初の手動起動は 2 回で、 1 回目(run 33230097557、2026-08-29 02:54Z)は 4 job 中 3 job が `API rate limit exceeded for installation` で失敗し、原因を直したあとの 2 回目(run 33233610215、同 04:21Z)が 4 job とも success だった (上表のとおり 3 job 定義に対して実行は 4 件になる。数え方が違うだけで、 どちらの数字も同じ run のものである)。 + +**その後 cron の経路も動いた**(2026-09-10 に `gh run list --workflow nightly.yml +--event schedule` で実測)—— **2026-08-29 から毎日、13 回**。うち 3 回 +(2026-08-31 / 09-01 / 09-02)が failure で、**直近 8 回(09-03〜09-10)は +すべて success** である。 + +**リポジトリ内でこの件数を書いてあるのは、ここと `README.md` / `README.ja.md` のnightly の段落の 3 箇所だけである。** README の 2 つは利用者向けで、ここを指すわけにいかないので**日付つきの記録として**書いてある(必須チェックの本数とまったく同じ扱いで、`CLAUDE.md` がその例外を 1 箇所に書いている)。**「ここ 1 箇所に限る」と書いていたが、それは同じ branch の中で既に偽だった** ——**排他を主張する文は、主張した本人がいちばん破りやすい。** + **「workflow ファイルが存在する」は「CI で実行された」ではない** —— -M2 の条件 7 をその基準で未達と判定した以上、こちらにも同じ基準を当てる。 -cron の経路そのものが動いたことは、まだ確かめられていない。 +M2 の条件 7 をその基準で未達と判定した以上、こちらにも同じ基準を当てた。 +**この項目は、その基準を当てたまま解消した側の例である** —— +**逆向きの陳腐化に注意すること。** 「まだ動いていない」と書いた記述は、 +動き始めた日から嘘になるのに、**赤くならない**(穴が埋まっても誰も知らせない)。 +実際この 1 文は 2026-09-10 まで 4 箇所に残り、**毎日走っている検査を +「存在しない」ことにしていた。** **上の表は「何が走るか」だけを書いている。「何が merge を止めるか」は ここには書かない。** 走ることと止めることは別で、しかも止める側は GitHub の @@ -709,7 +721,9 @@ v0.1.1 の Linux binary が上限に収まっているのは、Linux のビル **結論を先に書く。OpenCV 5 を土台にしている Unity 向けパッケージは、商用・OSS とも 本案以外に見つからない。** ただし本案が公開している API は `core` / `imgproc` / -`imgcodecs` / `objdetect` / `features` / `geometry` / `calib` / `stereo` に留まる(**本数は [所有権と versioning](./abi-ownership-and-versioning.md) §3 の冒頭が数える** —— ここに写すと 1 本増えた日にこの行だけが嘘になる)(M3.5 で `imgcodecs`、 +`imgcodecs` / `objdetect` / `features` / `geometry` / `calib` / `stereo` と、 +opt-in profile の `dnn`(M7c)に留まる(**module 名も本数も写さない正本は +`bindings/spec/*.json` のファイル名と [所有権と versioning](./abi-ownership-and-versioning.md) §3 の冒頭である** —— ここに写すと 1 本増えた日にこの行だけが嘘になる)(M3.5 で `imgcodecs`、 M5 の module 拡張で `objdetect` / `features` / `geometry` / `calib`、そして **2026-09 の API 拡張で姿勢・ArUco・imgproc の実用関数・core の基本演算・ 記述子マッチング・ステレオ視差**が加わった。**本数を数える正本は @@ -730,14 +744,14 @@ M5 の module 拡張で `objdetect` / `features` / `geometry` / `calib`、そし | --- | --- | --- | --- | | 1 | ~~**1 つの package に 1 platform 分の binary しか入らない**~~ **M3.5 で解消** | Unity は同じ package ID を 1 つしか導入できない。「エディタは Windows、実機は Android」が表現できなかった | **M3.5 完了**。全部入り tarball(`com.ayutaz.opencv-unity-native.tgz`)が配る正になり、Desktop 3 platform が同居した状態で `test-unity-tarball` が 16 passed | | 2 | ~~画像を encode / decode できない~~ **M3.5 で解消** | 比較した競合はいずれも画像の入出力を持つ(そちらはファイル経路まで含む)。**ここに「モジュールはリンク済みで、足りないのは ABI 関数だけ」と書いていたのは誤りで、実際は `imgcodecs` をリンクしていなかった**(下記 M5 節) | **M3.5 完了**(M5 から前倒し)。component を足し、`ocvu_imencode` / `ocvu_imdecode` を出した | -| 3 | ~~**OpenUPM に載っていない**~~ **解消済み(2026-08-30)** | OSS の Unity パッケージが探される場所。#1 に加えて asset 名と容量の条件がある | **(a) 版番号なしの asset 名・(b) 容量の検査・(c) 登録申請のすべてが済んだ。** openupm/openupm PR #6843 が自動マージされ、`https://package.openupm.com/com.ayutaz.opencv-unity-native` が配信している([登録の記録](./openupm-registration.md))。**配信されている版は v0.2.0 のままである** —— 登録と、新しい版が届くことは別 | +| 3 | ~~**OpenUPM に載っていない**~~ **解消済み(2026-08-30)** | OSS の Unity パッケージが探される場所。#1 に加えて asset 名と容量の条件がある | **(a) 版番号なしの asset 名・(b) 容量の検査・(c) 登録申請のすべてが済んだ。** openupm/openupm PR #6843 が自動マージされ、`https://package.openupm.com/com.ayutaz.opencv-unity-native` が配信している([登録の記録](./openupm-registration.md))。**配信されている版は `0.3.0` である**(2026-09-04 に確認。下の「配布 その 5」)—— 登録と、新しい版が届くことは別で、`0.2.0` から `0.3.0` へ切り替わるのに公開の約 4 時間を要した | | 4 | 検証している Unity が 1 版だけ | **M3.5 でその 1 版を 6000.0.82f1 → 6000.3.16f1 に載せ替えた**(6000.0 LTS の通常サポートが 2026-10 に終わるため。6.3 LTS は 2027-12 まで)。**版が 1 つしかないこと自体は変わっていない** | **M3.5 完了**(載せ替えのみ。複数版の検証は担当なし) | | 5 | ~~カメラ映像を受け取れない~~ **M4 で解消** | `WebCamTextureConverter`(3 overload)が `WebCamTexture` から `CvMat` を作る。**新しい C ABI 関数は 1 本も増えていない** —— 既存の上に立つ純 C# である | **解消済み(2026-08-30)** | | 6 | macOS で Unity に読み込ませたことがない | 「iOS のビルドに macOS runner が要るので M4 で自然に埋まる」と書いていたが、**埋まらなかった。** macOS runner は plugin をビルドするだけで Unity を起動しない | **未解消。2026-08-31 に 4 回試して「CI では閉じない」と確定した**(game-ci は macOS を支えず、Hub で直接入れる経路は Editor が 14 分で入るのにライセンスで止まる)。詳細は下の「担当が無かった制約」 | | 7 | Windows の IL2CPP を CI で回していない | 「game-ci では無理」の根拠に挙げていた issue は、使っていない別 action のものだった —— **そこで実際に投げて根拠を作った** | **未解消。2026-08-31 に「CI で回さない」と結論した** —— `windows-2022` で EditMode は 33 件通ったが、`Standalone` は `ToolchainNotFoundException` で落ちた(game-ci の Windows コンテナに MSVC が無い)。**根拠は実測であって他人の issue ではない。** これは「まだ調べていない穴」ではなく**意図して CI の外に置いたもの**である | | 8 | 対応 CPU アーキテクチャが狭い | Android エミュレータ(x86_64)が無いと開発しづらい | **M4 で決めた: arm64 のみ**(Android は arm64-v8a、iOS は実機の arm64)。**穴は塞いでいない —— 塞がないと決めた。** 増やすと platform が 2 つ増え、OpenCV のビルドも配布物も同じだけ増える(**数を写さない** —— 正本は `tools/dev.ps1` の `$AllPlatformBinaries` で、触る場所は `add-a-platform` skill にある) | | 9 | 「低コピー連携」を測っていない | §7 の 7 番目に掲げているのに実測が無い | **M7a で着手し、部分的に解消(2026-09-06)。** 割り当ては L3 が機械で保証し(ポインタ経路 0 バイト)、時間は測って公開したが**assert していない**。**`RenderTexture` / `AsyncGPUReadback` の経路は Editor(Mono)の `test-unity-graphics` でしか実行しておらず、そのレーンは CI に配線していない** —— IL2CPP の Player でこの経路が動くかは未実証のままである。**native texture pointer は評価のみで実装していない**(やらないと決めた)。判定と根拠は下の「M7a の判定」節 | -| 10 | 新しい DNN エンジンを載せていない | OpenCV 5 最大の変更。ただし Unity には代替がある。**2026-08-30 の調査で、5.0 に固定して作り込めない根拠が付いた**(根拠と一次情報は M7 節。**ここに再掲しない** —— 根拠を直すと 2 箇所が同時に古くなる) | **M7**(位置づけと、そこから出た module 分離の決定は下記) | +| 10 | 新しい DNN エンジンを載せていない | OpenCV 5 最大の変更。ただし Unity には代替がある。**2026-08-30 の調査で、5.0 に固定して作り込めない根拠が付いた**(根拠と一次情報は M7 節。**ここに再掲しない** —— 根拠を直すと 2 箇所が同時に古くなる) | **M7c で部分的に解消(2026-09-10)。** ONNX をメモリから読んで forward を 1 回走らせる C ABI 4 本を、opt-in profile(`OCVU_PROFILE_DNN`)として出した。**ただし実機で 1 度も動かしておらず、推論の速さも測っていない**(`### M7c の判定`)。位置づけと、そこから出た module 分離の決定は下記 | ### #1 を最優先に置く理由 @@ -1873,9 +1887,14 @@ Emscripten は既定で C++ 例外を無効にするので、`throw` は残る `NativeMethods.cs` が持つ 1 つの定数なので、そこの条件を 1 つ増やして済んだ。 **その 1 行が抜けていたのが M6 の欠陥 #1 で、ビルドもリンクも Player の起動も 通り、呼んだ瞬間に落ちた。** -- **依存 allowlist が新しい依存を捕まえる。** `calib` を足したとき、推移的に引かれた +- **依存 allowlist が捕まえるのは、artifact の中に独立したライブラリとして現れる依存だけである。** + `calib` を足したとき、推移的に引かれた `stereo` で 4 platform とも最初のビルドが落ちた(2026-09-02)。**Emscripten でも 同じ検査が働く** —— 落ちたら、引き込まれたものを確かめてから明示的に足す。 + **ただし限界がある**(M7c で実測)—— 別のライブラリの中へ静的に取り込まれた + third-party は allowlist に何も見えない。`dnn` が引き込む MLAS / ONNX Runtime は + `libopencv_dnn.a` の一部になっており、`protobuf` は捕まったのにこの 2 つは + 捕まらなかった(`### M7c の判定`)。 - **`OCVU_ABI_VERSION` は 1 のままである。** M5 は移設であって追加ではなく、 module を足しても版は動いていない。 @@ -1886,6 +1905,10 @@ Emscripten は既定で C++ 例外を無効にするので、`throw` は残る 懸念は**まだ消えていない。後払いの期限が来ただけである** —— **次にやることは配ることである**(「配布 その 5」)。 +→ **2026-09-04 に v0.3.0 として配った**(「配布 その 5」)。M4 / M5 / M6 の成果は +これで届き、この懸念は解消した。**同じ形は M7 で戻ってきている** —— **M7 の成果 +(`dnn` を含む)はまだどの公開版にも入っていない。** + --- ## M7 — Optional profiles と性能 @@ -1900,8 +1923,12 @@ DNN / contrib / 動画 codec / videoio が opt-in profile として追加でき 根拠と、それでも前倒ししない理由は [競合調査](./unity-opencv-integration-research-and-plan.md) §3 / §4.6 にある (要約すると、競合はすべて書き直し前のエンジンを載せている一方、Unity 利用者に -とっては推論エンジンが OpenCV だけではない)。**前倒しの判断は利用例が集まってから -行う。** +とっては推論エンジンが OpenCV だけではない)。~~**前倒しの判断は利用例が集まってから +行う。**~~ → **前倒しはしなかったが、M7 の中で M7c として実際に出した(2026-09-10)。** +着手の根拠は「利用例が集まったこと」でも「上流が安定したこと」でもなく、 +**リポジトリ所有者が 5.1 での作り直しの費用を承知のうえで受け入れると判断したこと** +である(下の「この決定は解除された」)。**この節が挙げる上流の実測は、いまも +そのまま成立している。** **低コピー経路の実測もここが担当である**(穴 #9)。掲げている主張を支える実測が まだ無い、という穴である。 @@ -2023,7 +2050,9 @@ M3.5 節を参照)、`ocvu_imencode` / `ocvu_imdecode` を出した。ここ `native/modules.cmake` の `OCVU_MODULES` が `OCVU_SOURCES` に組み込む module を選び、 除いた module の関数は binary から消える —— `tools/verify-exported-symbols.ps1` が 配布 binary の export 面と spec の完全一致を CI で見ており(3 platform とも `ocvu_` - の export 53 本が spec の 53 本と一致)、`OCVU_MODULES` を意図的に絞ると外した + の export が spec の関数一覧と過不足なく一致した。**本数をここに写さない** —— + 検査の性質は数に依らないし、正本は `docs/api-map.md` の冒頭である)、 + `OCVU_MODULES` を意図的に絞ると外した module の関数が実際に export 面から消えることも確認済みである。**target はいまも 1 つのままで、これは実装漏れではなく分けられないからである** —— `native/CMakeLists.txt` は同じソースを 2 回コンパイルする(配布物の `opencv_unity_native` に @@ -2047,9 +2076,15 @@ M3.5 節を参照)、`ocvu_imencode` / `ocvu_imdecode` を出した。ここ 問うた —— 両 assembly が実際にコンパイルされ(`Library/ScriptAssemblies/` に両方の `.dll` が現れた)、define を外すとどちらも消えることを EditMode の `ProfileGatingTests`(4 件)で確かめた。**証明したのは「機構が働くこと」であって - 「dnn で働くこと」ではない** —— `bindings/spec/dnn.json` はまだ無く、非 `standard` + 「dnn で働くこと」ではない** —— **~~`bindings/spec/dnn.json` はまだ無く、非 `standard` profile を宣言する module は現時点で 1 つも無いので、`CvUnity.Interop.Dnn` と - `CvUnity.Tests.Shared.Dnn` はどちらも中身が空の assembly のままである。 + `CvUnity.Tests.Shared.Dnn` はどちらも中身が空の assembly のままである。~~** + → **M7c で本物の `dnn` が入った(2026-09-08〜10、`e1b0930`)。** `bindings/spec/dnn.json` + が `"profile": "dnn"` を宣言し、C ABI 4 本が `NativeMethodsDnn` と `CvUnity.Dnn.CvDnn` + まで通っている —— **両 assembly はもう空ではない**(`### M7c の判定`)。 + **取り消し線の 2 文を消さずに残してあるのは、同じ「まだ無い」という前提が別の場所に + 残っていないかを次に読む人が確かめられるようにするためである**([所有権と versioning](./abi-ownership-and-versioning.md) + §4 が同じ形で処理している)。 3. **OpenCV の版を跨げるようにする。** 構成ハッシュには tag が入るので、tag を変えれば 古い artifact は使われなくなる(tag は M1 から入っている。M3 Task 1 が足したのは `Platform` である)。**しかしこれは「2 つの版が同時に成立する」 @@ -2064,13 +2099,23 @@ M3.5 節を参照)、`ocvu_imencode` / `ocvu_imdecode` を出した。ここ `tools/tests/OpenCvConfig.Tests.ps1` **つまり「確認」ではなく、config に軸を 1 本増やす設計作業である。** -4. **`dnn` を allowlist に足すのは、上の 1〜3 が済んでから。** 現在の `Modules` は - `core / imgproc / imgcodecs / objdetect / features / calib` で **dnn は入っていない**。足すと OpenCV 側の - ビルド時間と成果物サイズが変わる。**あわせて `THIRD_PARTY_NOTICES.md` の作業が要る** —— +4. **`dnn` を allowlist に足すのは、上の 1〜3 が済んでから。** ~~現在の `Modules` に + **dnn は入っていない**。~~ 足すと OpenCV 側のビルド時間と成果物サイズが変わる。 + **あわせて `THIRD_PARTY_NOTICES.md` の作業が要る** —— 同文書が明文で指示している: *"If a future `Modules` list adds `dnn` or `gapi`, re-run these searches — they will very likely start matching, and these two need to move up into the reproduced sections above."* dlpack と flatbuffers はいま「ライセンスディレクトリにあるが リンクされていない」側に分類されており、**`dnn` を足すとその分類が崩れる**(protobuf も入る)。 + + → **M7c で足した(2026-09-08)。** `tools/opencv-config.psd1` の `Modules` に `dnn` が + 入っている(**一覧をここに写さない。正本は同ファイルである**)—— 構成ハッシュが変わり、 + 全 platform 分の OpenCV を作り直した。**予告した notice の作業も実際に起きた**: + `protobuf` が新しく入り、**`flatbuffers` は「あるがリンクされていない」側から + リンク済みの側へ移った**(`dnn` の TFLite importer が読む側で使う。実物の + `opencv_dnn` を Windows / Linux の 2 つの名前修飾で grep して確かめてある)。 + **`dlpack` は移らなかった** —— どの `.a` にも 1 件も現れないので、いまも + 「あるがリンクされていない」側にある。**予告は「2 つとも崩れる」だったが、 + 実際に崩れたのは 1 つだけである。** 5. **CUDA / cuDNN を同梱するなら、2 つの前提条件を先に潰す。** どちらも技術判断ではない。 - **再配布の可否(未確認)。** 「本体が Apache-2.0」と「binary 内の全依存が Apache-2.0」は @@ -2244,7 +2289,7 @@ CI 自身での確認は `.superpowers/sdd/2026-09-05-m7c-dnn-profile/task-6-rep ### M7a の判定(2026-09-06。**完了条件 5 件のうち 2 件を扱う**) -**M7 は当初 3 つの計画に分ける想定だった**(`docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md`。M5 で「生成の仕組みと module 追加を同時にやると切り分けられない」と判断したのと同じ理由)—— **実際に実行されたのは M7a と M7b の 2 計画で、条件 5 は計画を経ないドキュメント上の決定として閉じた**。**M7a が担当するのは完了条件 2(低コピー経路の評価)と 3(benchmark の公開)だけである** —— 条件 1(profile ごとの native artifact 等)は当時まだどの計画の担当にもなっておらず、条件 4(C ABI / C# の module 分離)は M7b、条件 5(CUDA / cuDNN の再配布確認)は `### CUDA / cuDNN 同梱の判定` が担当する。**この節はその 2 件だけの判定であって、M7 全体の判定ではない** —— 条件 1・4・5 がそれぞれ何本閉じているかは、この節ではなく担当する計画・節自身の判定にある(条件 4 は `### M7b の判定`、条件 5 は `### CUDA / cuDNN 同梱の判定`、条件 1 は `### M7c の判定`)。ここに残数を書かないのは、担当する計画が閉じるたびにその数だけがこの節に取り残されて古くなるからである。 +**M7 は当初 3 つの計画に分ける想定だった**(`docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md`。M5 で「生成の仕組みと module 追加を同時にやると切り分けられない」と判断したのと同じ理由)—— **3 つとも実行され(M7a / M7b は `be5615f`、M7c は `e1b0930`)、条件 5 だけが計画を経ないドキュメント上の決定として閉じた**(**この文はこの節を書いた 2026-09-06 の時点では「実行されたのは 2 計画」だった。M7c が 2026-09-10 に実行されて古くなり、直した** —— 判定節どうしの散文の参照が腐る、というこの branch が繰り返し踏んだ形の 4 例目である)。**M7a が担当するのは完了条件 2(低コピー経路の評価)と 3(benchmark の公開)だけである** —— 条件 1(profile ごとの native artifact 等)は当時まだどの計画の担当にもなっておらず、条件 4(C ABI / C# の module 分離)は M7b、条件 5(CUDA / cuDNN の再配布確認)は `### CUDA / cuDNN 同梱の判定` が担当する。**この節はその 2 件だけの判定であって、M7 全体の判定ではない** —— 条件 1・4・5 がそれぞれ何本閉じているかは、この節ではなく担当する計画・節自身の判定にある(条件 4 は `### M7b の判定`、条件 5 は `### CUDA / cuDNN 同梱の判定`、条件 1 は `### M7c の判定`)。ここに残数を書かないのは、担当する計画が閉じるたびにその数だけがこの節に取り残されて古くなるからである。 実装は `.superpowers/sdd/2026-09-05-m7a-low-copy-and-benchmarks/`(Task 1〜6)。実測はすべてこのマシン(Windows 10.0.22631、X64、Unity 6000.3.16f1、2026-09-05〜09-06)。詳細な数字と読み方は [性能](./performance.md) が正本で、ここには写さない。 @@ -2266,7 +2311,7 @@ CI 自身での確認は `.superpowers/sdd/2026-09-05-m7c-dnn-profile/task-6-rep | # | 完了条件 | 判定 | | --- | --- | --- | -| 4 | `dnn` を足す前に、C ABI と C# の module 分離が済んでいること(上の決定 1〜2) | **満たした。ただし「機構が通っている」であって「dnn を分離した」ではない。** native 側は `native/modules.cmake` の `OCVU_MODULES` が module 単位でソースを選べる形になり、`tools/verify-exported-symbols.ps1` が binary の export 面を spec と完全一致で照合する(desktop 3 platform とも `ocvu_` の export 53 本が spec の 53 本と一致。`ci-native.yml` で実測。**配る経路(`release.yml`)にも配線してあるが、そちらの実測はこの PR の CI が初回である**)。C# 側は spec の `profile` が非 `standard` の module を別 assembly(`CvUnity.Interop.Dnn` / `NativeMethodsDnn` / `Runtime/Interop.Dnn/`)へ出す生成器の分岐と、それを Unity に問う EditMode の `ProfileGatingTests` を作った——define(`OCVU_PROFILE_DNN`)を立てて両 assembly(`CvUnity.Interop.Dnn` と、その到達性テストを持つ `CvUnity.Tests.Shared.Dnn`)が実際にコンパイルされ、外すと両方消えることを Unity 自身に実測した。**さらに、合成した `profile: "dnn"` の spec から生成した実物のファイル 2 つ(`NativeMethods.Dnnprobe.g.cs` と `AbiReachabilityChecks.Dnn.g.cs`)が、define を立てた Unity で実際にコンパイルされることまで実測した**(2026-09-06。`Library/ScriptAssemblies/` に両 dll が現れた)—— **この一段は最終レビューで足した。** それまで確かめられていたのは「`defineConstraints` が**手書きの**コードを切る」ことまでで、**生成物を誰もコンパイルしていなかった**(その穴を通って欠陥が 1 件入っていた。下の「穴を隠さず書く」を参照)。**dnn の spec も実装もまだ無い** —— `bindings/spec/dnn.json` は存在せず、非 `standard` profile を宣言する module は現時点で 0 個で、`CvUnity.Interop.Dnn` / `CvUnity.Tests.Shared.Dnn` はどちらも commit された状態では中身が空である。決定の詳細は上の「決定: native bridge を module 単位に分ける」1・2、規約は [所有権と versioning](./abi-ownership-and-versioning.md) §4 | +| 4 | `dnn` を足す前に、C ABI と C# の module 分離が済んでいること(上の決定 1〜2) | **満たした。ただし「機構が通っている」であって「dnn を分離した」ではない。** native 側は `native/modules.cmake` の `OCVU_MODULES` が module 単位でソースを選べる形になり、`tools/verify-exported-symbols.ps1` が binary の export 面を spec と完全一致で照合する(desktop 3 platform とも `ocvu_` の export が spec の関数一覧と過不足なく一致。当時は 53 本で、**その後 M7c の `dnn` が加わって増えた —— 本数の正本は `docs/api-map.md` の冒頭である**。`ci-native.yml` で実測。**配る経路(`release.yml`)にも配線してあるが、そちらの実測はこの PR の CI が初回である**)。C# 側は spec の `profile` が非 `standard` の module を別 assembly(`CvUnity.Interop.Dnn` / `NativeMethodsDnn` / `Runtime/Interop.Dnn/`)へ出す生成器の分岐と、それを Unity に問う EditMode の `ProfileGatingTests` を作った——define(`OCVU_PROFILE_DNN`)を立てて両 assembly(`CvUnity.Interop.Dnn` と、その到達性テストを持つ `CvUnity.Tests.Shared.Dnn`)が実際にコンパイルされ、外すと両方消えることを Unity 自身に実測した。**さらに、合成した `profile: "dnn"` の spec から生成した実物のファイル 2 つ(`NativeMethods.Dnnprobe.g.cs` と `AbiReachabilityChecks.Dnn.g.cs`)が、define を立てた Unity で実際にコンパイルされることまで実測した**(2026-09-06。`Library/ScriptAssemblies/` に両 dll が現れた)—— **この一段は最終レビューで足した。** それまで確かめられていたのは「`defineConstraints` が**手書きの**コードを切る」ことまでで、**生成物を誰もコンパイルしていなかった**(その穴を通って欠陥が 1 件入っていた。下の「穴を隠さず書く」を参照)。**dnn の spec も実装もまだ無い**(**2026-09-06 時点の話である。M7c が 2026-09-10 に `bindings/spec/dnn.json` と C ABI 4 本を入れたので、いまは在る —— `### M7c の判定`**)—— 当時は `bindings/spec/dnn.json` が存在せず、非 `standard` profile を宣言する module は 0 個で、`CvUnity.Interop.Dnn` / `CvUnity.Tests.Shared.Dnn` はどちらも commit された状態では中身が空だった。決定の詳細は上の「決定: native bridge を module 単位に分ける」1・2、規約は [所有権と versioning](./abi-ownership-and-versioning.md) §4 | **穴を隠さず書く。** @@ -2315,6 +2360,85 @@ CI 自身での確認は `.superpowers/sdd/2026-09-05-m7c-dnn-profile/task-6-rep - **`PNG_ARM_NEON=off` は取引であって、無条件の勝ちではない。** `cb250c2` で android-arm64 / ios-arm64 / macos-arm64 の 3 platform に限定して立てた。原因は上流 OpenCV 5.0.0 の vendoring 欠陥——`3rdparty/mlas/lib/compute.cpp` の `MlasGQASupported` が `MlasHGemmSupported()` を無条件に呼ぶが実体が vendor されておらず、macOS arm64 のリンクが `MlasHGemmSupported` 未定義で落ちる。その ASM 有効化の真因を辿ると `3rdparty/libpng/CMakeLists.txt` の ARM NEON 向け `enable_language(ASM)` に行き着き、`PNG_ARM_NEON=off` はこれを止める代わりに `arm/filter_neon.S` だけでなく同じ分岐にある `arm_init.c` / `filter_neon_intrinsics.c` / `palette_neon_intrinsics.c` も道連れにする——**PNG は arm64 3 platform で ARM 加速を丸ごと失う。** 上流には intrinsics だけ残す形(QNX 向け)があるが、このプロジェクトからは触れない。5.1 で上流が直せば見直す価値がある。 - **dnn の到達性テストは、レビューまでどこからも呼ばれていなかった。** 生成物 `AbiReachabilityChecksDnn.CallEveryEntryPoint()`(`tests/UnityProject/Assets/Tests/Shared.Dnn/`)は spec の dnn 4 関数を 1 回ずつ呼ぶために存在するが、標準 profile の同じ仕組み(`AbiSurfaceTests.cs` / `AbiSurfacePlayerTests.cs` / `WebSmokeRunner.cs` の 3 箇所から呼ばれる)と違い、呼び出し元が 1 つも無かった。**呼ばれない宣言そのものが IL2CPP の stripping の対象になる**ので、この検査は「無い」以上に悪い状態だった——M4 が手書きの 19 本のうち 7 本で実際に踏んだのと同じ形の穴が、dnn の 4 本に開いたまま気づかれずにいた。レビューで指摘され、`tests/UnityProject/Assets/Tests/PlayMode/AbiSurfaceDnnPlayerTests.cs`(`#if OCVU_PROFILE_DNN` で自分を守る、`CvUnity.Tests.PlayMode` の asmdef に `CvUnity.Tests.Shared.Dnn` への参照を足した)を追加して閉じた。**手で 1 回、`OCVU_PROFILE_DNN` を立てて `test-unity-player` を実際に走らせ、stripping が dnn の 4 宣言を消していないことを確認した**(`ProfileGatingTests` が確立した「正の方向は人が手で確かめる」規約と同じ形。実測の日付と結果はこの節の下、または `tools/dev.ps1` 実行ログを参照)。**CI はどのレーンも `OCVU_PROFILE_DNN` を立てないので、この確認は一度きりであり、次に dnn の ABI が変わったときに自動では再検証されない。** +### M7 の判定(2026-09-10。**5 件すべてを満たした**) + +**M7 はこのリポジトリで最後のマイルストーンである。** この文書の `## M` 見出しは +M0 から M7 までで、M8 は無い。**帰結を先に書く: ここで「次のマイルストーンへ送る」 +と書いた留保は、行き先が存在しない。** 送るのではなく、担当が無いなら無いと書く +(下の「満たしたが、実証していないこと」と「担当が無い」)。 + +**判定が 1 節にまとまっていないのは、M7 だけである。** M0〜M6 は 1 つの節が +その全条件を判定しているが、M7 は 3 つの計画と 1 つの文書上の決定に割れたため、 +**5 件の判定が 4 節に分かれている。** この節はその索引であって、判定の本文では +ない —— **各条件の判定と根拠は担当する節にあり、ここには写さない**(写すと、 +どちらかが直された日にもう一方だけが古くなる)。 + +| # | 完了条件 | 判定の本文 | 満たした | 実証した | +| --- | --- | --- | --- | --- | +| 1 | profile ごとの native artifact、manifest、third-party notices | `### M7c の判定`(2026-09-10) | はい | **いいえ** —— `dnn` を実機で動かしたことは 1 度も無く、推論の速さも測っていない | +| 2 | `RenderTexture` / native texture pointer / `AsyncGPUReadback` を使う低コピー経路の評価 | `### M7a の判定`(2026-09-06) | はい | **部分的** —— `ToMat` / `RequestMat` の画素を運ぶ経路は Editor の `test-unity-graphics` でしか走っておらず、そのレーンは CI に配線されていない。native texture pointer は評価のみで実装していない | +| 3 | package size、startup time、frame time、allocation の benchmark を公開 | `### M7a の判定`(2026-09-06) | はい | **半分** —— package size と allocation は機械が assert し CI が守る。frame time と startup time は公開するが assert しない(設計 D1)。startup time は「native ライブラリの真の初回ロード」を捉えていない | +| 4 | `dnn` を足す前に、C ABI と C# の module 分離が済んでいること | `### M7b の判定`(2026-09-06) | はい | はい —— ただし当時証明したのは「機構が働くこと」で、`dnn` で働くことは条件 1 の側(M7c)が示した | +| 5 | CUDA / cuDNN を同梱するなら、再配布条件の確認が済んでいること。確認できないなら**同梱しない**と決めて記録する | `### CUDA / cuDNN 同梱の判定`(2026-09-06) | はい | —— **ライセンス条項は読んでいない。** 条件が用意した 2 経路のうち「確認できないので同梱しないと決める」を選んだ形である | + +**「満たした」と「実証した」を同じ列に並べないのは、この 2 つが M7 では実際に +食い違うからである。** 5 件とも「満たした」だが、そのうち **1 件は実機で 1 度も +動かしておらず、2 件は CI が見ていない経路を含み、1 件は根拠となる文書を読んで +いない。** 完了条件は「作ってあること」を問うており、「動くと確かめてあること」 +までは問うていない —— その差が M7 では M4 以来いちばん大きい。 + +**満たしたが、実証していないこと**(本文は各判定節の「穴を隠さず書く」にある。 +**ここは索引であって、詳細を写さない**) + +- **`dnn` は実機で 1 度も動いていない。** M4 が残した「Android / iOS は CI が + ビルドするが誰も動かしたことがない」という穴が、そのまま `dnn` にも当てはまる。 + Web は CI の browser E2E に任せており、手元では確かめていない +- **`ocvu_dnn_net_forward` の `.clone()` を要ると示す再現テストが無い。** + 外しても検知できず、しかも**確定させる経路がリポジトリの構成上存在しない** + (復元する OpenCV の木にヘッダと lib はあるが `Net::forward()` の実装ソースが無い)。 + 「負の対照が取れない」の 3 例目であり、これまでで最も弱い形である +- **MLAS / ONNX Runtime は依存 allowlist から見えない。** allowlist が検査するのは + artifact の中の独立したライブラリで、`libopencv_dnn.a` に静的に取り込まれたものは + 現れない。`protobuf` は捕まったが、この 2 つは捕まらない +- **`WITH_CAROTENE` と `WITH_KLEIDICV` は既定 ON のまま監査していない。** + 実際にリンクされていることは実測済みだが、ライセンス・再配布条件は確認していない +- **third-party のライセンス集合は platform ごとに実際に違うのに、`THIRD_PARTY_NOTICES.md` + は 1 通で全 platform を代表している**(`clapack-lapack_LICENSE` は macOS / iOS に無い) +- **CUDA / cuDNN の再配布条件は読んでいない。** 大きさだけで結論が出たので読まずに + 済んだ、という形である —— **配布形態が変われば、この宿題はそのまま戻ってくる** +- **`dnn` の到達性は人が手で 1 回確かめたきりである。** `OCVU_PROFILE_DNN` を立てる + workflow も `dev.ps1` のレーンも無いので、次に `dnn` の ABI が変わっても自動では + 再検証されない + +**担当が無い。** 上の各項目は、M8 が無い以上「次のマイルストーンで拾う」ことが +できない。**拾う予定は無い** —— 拾うなら、そのときに新しい計画を立てることになる。 +**実機の検証だけは手順書がある**([実機での検証手順](./m4-device-verification.md)。 +ただし `dnn` の項は無い)。 + +**M7 で新しく分かった、記録に値する形が 4 つある。** + +1. **1 つのマイルストーンを複数の判定節に割ると、節どうしの名指し参照が腐る。** + `ci-lint` の documentation link check は通常の markdown リンクしか見ないので、 + 見出しを backtick で引用しただけの参照は対象外である。**警告を書いたその branch の + 中で、さらに 1 件を 2 人がかりで見落とした**(`### CUDA / cuDNN 同梱の判定`)。 + だから 4 節とも「ここに残数を書かない」を明文にしてある +2. **検査が当たっていた対象が、本番の物ではなく代用物だったことが 2 件。** + `verify-exported-symbols.ps1` は「配布 binary の公開面」を主張しながら + `ci-native` にしか配線されておらず、当たっていたのは開発用の binary だった + (`release.yml` にも配線して閉じた)。profile の gating を Unity に問う正の対照は + **手書きの probe** で満たされており、生成物を 1 度もコンパイルしていなかった —— + その穴を通って `[DllImport(LibraryName, ...)]` が CS0103 になる欠陥が入った +3. **「binary に入っている」と「C# 側がコンパイルする」は別である。** + `dnn` の native 実装は既定の binary に必ず入っており、切っているのは C# の + assembly だけである。M3.5 で踏んだ「OpenCV に入っている」と「このプラグインが + リンクしている」の取り違えと同じ形が、1 段上の層で再現した +4. **生成物が増えても検知範囲が自動で広がった実例。** M7c の新しい生成物 3 つは、 + `check-generated-file-edit.sh` を 1 行も触らずに検知範囲へ入った(生成物が + 先頭 5 行で名乗る規約を見る設計)。**対照的に、一覧を写していた + `check-unityengine-leak.sh` は M7c で守備範囲が 2 → 4 フォルダになったのに + 2 のままで、2026-09-10 に正本から読む形へ直した**(実測: 直す前は + `Runtime/Interop.Dnn` と `Runtime/Dnn` が素通りした) + --- ## 担当が無かった制約(2026-08-29 に全件割り当て、うち 1 件は M3.5 で解消) @@ -2611,6 +2735,9 @@ M0 ハーネス ──> M1 OpenCV ビルド ──> M2 Windows slice ──> M3 | v M7 profiles + (5 件すべて達成。判定は M7a / M7b / CUDA / M7c の 4 節に分かれ、 + 索引は「M7 の判定」。M7 が最後のマイルストーンで、後続は無い。 + 成果はまだどの公開版にも入っていない) ``` **配布はマイルストーンではないが、マイルストーンの間に必ず挟まる。** M3 が v0.1.0 / diff --git a/docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md b/docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md index ff760c1..6cf6302 100644 --- a/docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md +++ b/docs/superpowers/plans/2026-09-05-m7-profiles-and-performance.md @@ -1,5 +1,12 @@ # M7 — Optional profiles と性能: 設計(3 計画が共有する正本) +> **3 計画とも実行済みである((a) / (b) は `be5615f`、(c) は `e1b0930`)。 +> 以下は着手前の記録として読むこと。** M7 の完了条件 5 件はすべて満たされ、 +> 判定は `docs/roadmap.md` の `M7a の判定` / `M7b の判定` / +> `CUDA / cuDNN 同梱の判定` / `M7c の判定` の 4 節に分かれている(索引は +> 同文書の `M7 の判定`)。**M7 はこのリポジトリで最後のマイルストーンで、 +> 後続は無い** —— この文書が「後の計画へ送る」と書いた項目には、送り先が存在しない。 + **この文書は仕様であって計画ではない。** 実行手順は 3 つの計画が持つ: | | 計画 | 何を成立させるか | @@ -9,7 +16,9 @@ | **(c)** | [`2026-09-05-m7c-dnn-profile.md`](./2026-09-05-m7c-dnn-profile.md) | `dnn` を opt-in profile として足す(完了条件 1) | **依存は (b) → (c) の 1 本だけである。** (a) はどちらにも依存せず、並行して進められる。 -**(c) は (b) が済むまで着手してはならない** —— roadmap の決定 4 が明文でそう定めている。 +**~~(c) は (b) が済むまで着手してはならない~~** —— roadmap の決定 4 が明文でそう定めていた。 +**この gate は満たされた**((b) は 2026-09-06、所有者による解除は 2026-09-08。roadmap の +「この決定は解除された」)。 --- @@ -21,7 +30,7 @@ | 2 | RenderTexture / native texture pointer / AsyncGPUReadback を使う低コピー経路の評価 | **(a)** | | 3 | package size、startup time、frame time、allocation の benchmark を公開 | **(a)** | | 4 | `dnn` を足す前に、C ABI と C# の module 分離が済んでいること | **(b)** | -| 5 | CUDA / cuDNN を同梱するなら、再配布条件の確認が済んでいること。確認できないなら**同梱しないと決めて記録する** | **(c)** | +| 5 | CUDA / cuDNN を同梱するなら、再配布条件の確認が済んでいること。確認できないなら**同梱しないと決めて記録する** | ~~**(c)**~~ → **計画を経ない文書上の決定**(`docs/roadmap.md` の `### CUDA / cuDNN 同梱の判定`。**この行が (c) を担当と書いていたのは誤りで、(c) の Task 1〜6 に条件 5 の作業は無い。正本は roadmap である**) | **条件 5 の結論は先に出ている。** roadmap が実測を持っている —— cuDNN は 1 platform あたり **698〜772 MB**(PyPI `nvidia-cudnn-cu12` 9.25.1.1、2026-08-30 実測)で、 @@ -80,7 +89,7 @@ cuDNN は 1 platform あたり **698〜772 MB**(PyPI `nvidia-cudnn-cu12` 9.25. ### 進め方 - **すべての実装を TDD で行う。** 失敗するテストを先に書き、赤いことを確かめてから実装する -- **main で直接作業しない。** 現在のブランチは `feat/m7-profiles-and-performance` +- **main で直接作業しない。** 当時のブランチは `feat/m7-profiles-and-performance`(**もう存在しない**) - **`dev.ps1` のレーンは相互排他である。2 つ同時に走らせないこと。** 結果を書くレーンは開始時に `artifacts/test-results/` を**ディレクトリごと消す**ので、 後から始めたほうが先行しているほうの結果を消す。**先行したレーンは赤くならず無音で止まる** @@ -266,7 +275,7 @@ dnn の宣言は `CvUnity.Interop.Dnn` assembly の | --- | --- | | **CUDA / cuDNN の同梱** | 1 platform 分で配布上限を超える(上記 §1)。(c) が「同梱しない」と記録する | | **native rendering plugin(`IssuePluginEvent`)** | グラフィックス API ごとに 6 platform 分の実装が要る新しい subsystem(D4) | -| **`gapi` / `videoio` / 動画 codec** | M7 のゴールには挙がっているが、`dnn` より優先度が低く、FFmpeg / GStreamer の再配布条件を別途確認する必要がある。**(c) の後に別計画で扱う** | +| **`gapi` / `videoio` / 動画 codec** | M7 のゴールには挙がっているが、`dnn` より優先度が低く、FFmpeg / GStreamer の再配布条件を別途確認する必要がある。~~**(c) の後に別計画で扱う**~~ → **担当が無い。M7 の後にマイルストーンは無く、この項目を引き取る計画は存在しない**(やるなら、そのときに新しく計画を立てることになる) | | **OpenCV の版を跨いだ並走**(roadmap 決定 3) | 「確認」ではなく config に軸を 1 本増やす設計作業で、版文字列を直に assert している 5 箇所の書き換えを伴う。**M7 の完了条件に入っていない** | | **`OCVU_ABI_VERSION` の分割** | 単一の整数のままにする決定は `docs/abi-ownership-and-versioning.md` §2 が正本で、**ここで再オープンしない** | diff --git a/docs/superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md b/docs/superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md index f67742e..a83b2c2 100644 --- a/docs/superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md +++ b/docs/superpowers/plans/2026-09-05-m7a-low-copy-and-benchmarks.md @@ -2,6 +2,12 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> **この計画は実行済みである(2026-09-06、`be5615f`)。完了条件 2・3 を満たした。** +> **以下は着手前の記録として読むこと。** 判定と、そこで記録した穴 +> (`RenderTexture` の経路が CI に配線されていないこと、`test-unity-player` が +> このマシンで後始末段階からハングすること)は `docs/roadmap.md` の +> `M7a の判定` にある。**M7 の 4 つの計画には、すべて同じ趣旨の枠を置いてある。** + **Goal:** Unity のテクスチャと `CvMat` の間の低コピー経路を実測で評価し、割り当て・package size を機械が守る形で assert し、時間は公開だけする benchmark レーンを作る。 **Architecture:** 測定は 2 層に分ける。**決定的な量(割り当てバイト数)は L3(素の .NET)で `GC.GetAllocatedBytesForCurrentThread()` を使って assert し、CI が落とす。** Unity 側(Texture2D / RenderTexture)は正しさを assert し、時間は機械可読な 1 行として吐いて `tools/` のスクリプトが読んで**公開する**。native texture pointer は D4 の決定に従い、実装せず評価だけを記録する。 diff --git a/docs/superpowers/plans/2026-09-05-m7b-module-separation.md b/docs/superpowers/plans/2026-09-05-m7b-module-separation.md index 70edec1..3c7d6f3 100644 --- a/docs/superpowers/plans/2026-09-05-m7b-module-separation.md +++ b/docs/superpowers/plans/2026-09-05-m7b-module-separation.md @@ -2,6 +2,13 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> **この計画は実行済みである(2026-09-06、`be5615f`)。完了条件 4 を満たした。** +> **以下は着手前の記録として読むこと。** 本文にある「dnn の spec も実装もまだ無い」 +> 「非 `standard` profile を宣言する module は 0 個」といった記述は、**この計画の +> 実行時点の状態**である —— **M7c が 2026-09-08〜10 に `bindings/spec/dnn.json` と +> C ABI 4 本を入れたので、いまは在る**(`docs/roadmap.md` の `M7c の判定`)。 +> 隣の 2 つの計画(共有の仕様と (c))にも同じ趣旨の枠を置いてある。 + **Goal:** `dnn` のような opt-in profile を後から足せるよう、native の CMake target と C# の assembly を module 単位に割り、**割っても配布する binary の公開面が 1 バイトも変わっていないこと**を機械が守る形にする。 **Architecture:** 先に**安全網**を作る —— 配布する binary が実際にエクスポートしているシンボルを読み、`bindings/spec/*.json` の関数一覧と**完全一致**することを要求する検査を足す。この網があってはじめて、CMake の作り替えが「何も落としていない」と言える。そのうえで (1) ソース一覧を module ごとに割って `OCVU_MODULES` から組み立て、(2) spec に `profile` を持たせ、profile が既定でないものは別 assembly・別クラスへ出す、(3) その assembly を `defineConstraints` で切る。 diff --git a/docs/superpowers/plans/2026-09-05-m7c-dnn-profile.md b/docs/superpowers/plans/2026-09-05-m7c-dnn-profile.md index 4f073ab..aae77fc 100644 --- a/docs/superpowers/plans/2026-09-05-m7c-dnn-profile.md +++ b/docs/superpowers/plans/2026-09-05-m7c-dnn-profile.md @@ -2,7 +2,22 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. -> ## 着手する前に読むこと +> ## この gate は 2026-09-08 に解除され、この計画は実行済みである(`e1b0930`) +> +> **以下の「着手する前に読むこと」は、着手前の記録として残してある。** +> 前提 1((b) が済んでいること)は 2026-09-06 の `### M7b の判定` が、 +> 前提 2 は 2026-09-08 のリポジトリ所有者による gate lift が満たした +> (やり取りの引用と、その順序が意味を持つ理由は `docs/roadmap.md` の +> 「この決定は解除された」)。**したがって、この計画を「着手してはいけない +> 計画」として読まないこと。** 完了条件 1 を閉じたのはこの計画である +> (`### M7c の判定`)。 +> +> **元の決定を支えていた根拠は否定されていない** —— 上流の実測(旧エンジンの +> 削除、`enum EngineType` の再番号、`OPENCV_FORCE_DNN_ENGINE` の意味変化)は +> いまも成立しており、**5.1 が来たら作り直しが要ると見込んでよい。** +> 変わったのは、所有者がその費用を承知のうえで受け入れると判断したことだけである。 +> +> ## 着手する前に読むこと(2026-09-08 より前の記録) > > **`docs/roadmap.md` の M7 節は「5.0 に固定した DNN ラッパーを作り込まない」と > 決めている。** 根拠は上流の実測で、要約すると **5.0 で作り込むと 5.1 で diff --git a/docs/unity-opencv-integration-research-and-plan.md b/docs/unity-opencv-integration-research-and-plan.md index 84daec3..f701a1c 100644 --- a/docs/unity-opencv-integration-research-and-plan.md +++ b/docs/unity-opencv-integration-research-and-plan.md @@ -174,24 +174,26 @@ Unity 6 の managed plug-in サポート表では、.NET Standard と .NET Frame | 種別 | OpenCV for Unity | 本案(**この表は M3.5 時点の記録である**。その後 v0.3.0(2026-09-04)まで公開し、M4 / M5 / M6 の成果は届いている。**最新の公開版は `docs/roadmap.md` の「配布」の節が正本**) | | --- | --- | --- | -| モジュール | **30 以上**(`dnn` / `photo` / `ml` / `video` / `videoio` / `tracking` / contrib 各種を含む) | OpenCV としてビルドしているのは 6(`core` / `imgproc` / `imgcodecs` / `objdetect` / `features` / `calib`)。**プラグインがリンクしているのは 8 つ**(この 6 つに、依存として推移的に引かれる `geometry` と `stereo` を足したもの)で、C ABI に出ているのはさらにその一部である(**本数を数える正本は [API 対応表](./api-map.md) の冒頭**) | +| モジュール | **30 以上**(`dnn` / `photo` / `ml` / `video` / `videoio` / `tracking` / contrib 各種を含む) | **数も一覧もここに写さない** —— OpenCV としてビルドしている module の正本は `tools/opencv-config.psd1` の `Modules`、プラグインがリンクしている module の正本は `cmake/FindOpenCvUnityDeps.cmake` の `COMPONENTS` である(**写していた頃は実際に古くなった**: M7c が `dnn` を両方に足したのに、この行は「ビルド 6 / リンク 8」のままだった)。C ABI に出ているのはさらにその一部で、**本数を数える正本は [API 対応表](./api-map.md) の冒頭**である | | platform | Windows / macOS / Linux / Android / iOS / WebGL / UWP / ChromeOS / visionOS beta | **この行は M3.5 時点の記録**(Windows / macOS / Linux)。**その後 M4 で Android / iOS、M6 で Web が加わった** —— 現況は [ロードマップ](./roadmap.md) が持つ | | 配布 | Asset Store から 1 つ入れれば全 platform | **1 つの tarball に Desktop 3 platform 分**(M3.5 で解消。[ロードマップ](./roadmap.md)「差別化の穴」の 1 件目)。~~**モバイルはまだ入らない**(M4)~~ **2026-08-30 に入った** | | カメラ | `WebCamTexture` の補助クラス群、WebGPU 対応の非同期読み出し | 無し(`Texture2D` のみ) | -| 推論 | OpenCV DNN と Unity Sentis を切り替える `MultiBackendDnn` | 無し | +| 推論 | OpenCV DNN と Unity Sentis を切り替える `MultiBackendDnn` | **opt-in profile(`OCVU_PROFILE_DNN`)で ONNX の推論 1 経路**(M7c、2026-09-10)。メモリから ONNX を読み、blob 化して forward を 1 回走らせる。**実機・GPU・速度の実証は無い** | | サンプル | 多数 | 1 つ(`Samples~/BasicUsage`) | **推論について 1 点補足する。** Enox が Sentis との切り替えを用意しているのは、 **Unity 利用者にとって推論エンジンは OpenCV だけではない**からである。したがって 「OpenCV 5 の新しい DNN エンジンを載せれば勝てる」とは言えない。§3 に書いたとおり 差になり**うる**が、Unity の中では Sentis が代替になる領域であり、そこへ投資するかは -M7 で判断する。 +M7 で判断する。→ **M7c で載せた(2026-09-10)** —— ただし opt-in profile として最小限 +(ONNX の読み込み・blob 化・forward の 3 手)であり、**実機で 1 度も動かしておらず +推論の速さも測っていない**。Sentis との比較ができる状態ではない。 #### 配布経路: OpenUPM OSS の Unity パッケージは [OpenUPM](https://openupm.com/) 経由で探されることが多い。 -**本案はまだ登録していない**(申請には公開済みのリリースが 1 つ要るので、次の版のあとに -なる)。**登録できる形にする作業は M3.5 で終わっている** —— 下記の 2 つの制約はどちらも +**~~本案はまだ登録していない~~ 2026-08-30 に登録され、レジストリが配信している** +(openupm/openupm PR #6843 が自動マージされた。[OpenUPM への登録](./openupm-registration.md))。**登録できる形にする作業は M3.5 で終わっている** —— 下記の 2 つの制約はどちらも 満たした。手順と残りの条件は [OpenUPM への登録](./openupm-registration.md) にある。 登録の障害になると考えていたのは「binary を git の追跡外に置いている」点だが、