v0.6.21 — 判定器の欠陥を自分で見つけて直しました
v0.6.21 — 判定器の欠陥を、自分で見つけて直しました
⚠ 先に読むところ
1. 外部監査ではなく、自分の property-based 試験が見つけました
版を上げるかどうかを決める判定器(scripts/schemaLanguageDiff.mjs)に、
危険側の誤判定がありました。これまでの 4 件はすべて外部監査の指摘でしたが、
これは 5 件目で、初めてこちらの試験が先に見つけたものです。
ランダム生成した schema 対 961 件 → 危険側 4 件
計画(docs/V060_PLAN_20260805.md §3)が [AI] で
「小さな schema をランダム生成し、ajv の真値と判定器を突き合わせ続ける」と
約束したまま、10 日間そのまま落ちていた分です。
入れた 1 回目で反例が出ました。
2. 危険側でした。受け手が拒む向きの誤りです
旧 oneOf: [{type:'string'}, {$ref:'#/definitions/d0'}] definitions.d0.type = 'boolean'
新 同じ oneOf definitions.d0.type = ['boolean','null']
ajv null が旧 invalid → 新 valid = **言語が広がっている**
判定 HOLD(据え置き可) ← **上げるべきなのに上げない**
oneOf は「ちょうど 1 枝が一致」なので枝の言語について単調でなく、枝ごとの比較ができません。
そこで「変わったか」を JSON.stringify(o.oneOf) !== JSON.stringify(n.oneOf) で見ていました。
枝が $ref なら、参照先が変わっても枝の文字列は同じです。
受け手への影響。 版が据え置かれると、受け手は旧 schema を pin したままになります。
新しい版が null を出した瞬間、受け手の検証が落ちます。
「壊れたのはいつか」ではなく「使い始めたのはいつか」で止まる——
この判定器が最初から避けようとしていた形そのものです。
root が
$refの場合は入口で辿るので前から正しく、oneOfの枝だけが素通りしていました。
3. 回帰テストを先に入れてから直しました
v0.5.1 / v0.5.2 と同じ順序です。
① 回帰テストを入れる(条文 ①-c3) → 2 件落ちることを実測
② 判定器を直す(比較の前に $ref を展開)
③ 5 件すべて通ることを確認
④ 修正を戻すと 3 件落ちることを実測
直す前に落ちることを見ていなければ、その試験が欠陥を捕まえた証拠になりません。
4. 独立生成では判別力がありませんでした
最初は schema を独立に 2 枚生成する形にしました。実測すると 98% が BUMP です。
独立生成 600 対 BUMP 589 / HOLD 9 / HOLD_RECORD 2
これでは「常に BUMP と言う判定器」も同じ点数を取ります。
危険は小さな変更に潜むので、1 か所だけ変異させる形へ変えました。
変異対 961 対 BUMP 779 / HOLD_RECORD 173 / HOLD 9 ← ここで 4 件見つかった
変異が当たらなかった対は数えていません(539 件を除外)。
当たらなかった変異と素通りした変異は出力が同じなので、混ぜると意味が消えます。
5. 実装済みの道具を「未実装」と 9 日間書いていました
公開文書 2 か所が「npm run fit:contacts の 4極版は未実装」と書いていましたが、
実装済みでした(往復誤差 0 mm で動きます)。
11:21 6e151bc docs 「4極版は未実装」と書いた
11:38 7602666 script **その 17 分後**に fitContactsTrrs.ts が入った
15:53 18abe44 docs 同じ文書を再び触ったが、直っていない
VERIFICATION_PLAN.md は測ってくださる方へ向けた文書です。
在る道具を「無い」と伝え、手計算を促していました。
なぜ 9 日も残ったか。 この repo の文言検査は「在ると言ったのに無い」だけを見ていました
(指したパスが実在するか)。反対向きには検査が 1 つもありませんでした
(実測 0 件/対照は 13 ファイル)。
害は受け手の側にしか出ません。書いた本人は道具が在ることを知っているので、
読み返しても違和感がない。しかも実装が進むほど増えます。
同じ型を機械で止める検査を入れました。
版数を上げていない根拠(すべて実測)
toolVersion 20 / schemaVersion 2 のままです。
判定器は配布物か **違う**(releaseAssets に 0 回。対照: 検算ツールは 2 回)
配る検算ツール v0.6.20 と **byte 一致**
実 schema での判定 **179 対中 0 件が変化**(現行 schema × 直近 8 tag)
CLI 出力の 10 経路 基準と byte 一致
判定器は配布物ではありません。受け手が回すのは verifyReleaseSourceInputs.mjs で、
そちらは 1 バイトも変わっていません。判定器はこちらが版を決めるための道具です。
oneOf の枝が $ref の形では
判定が HOLD → BUMP へ変わります。それが今回直した欠陥です。
現行 schema にその形が無いので、実 schema での判定が変わらなかっただけです。
まだやっていないこと
- **実測が 1 件も来ていません。**ジャック内部の接点位置は全部仮定のままで、
導通測定も音響測定もしていません(verifiedPhysicalはfalse)。 - BusyBox を oracle に足すこと。開発機にも CI にもありません。
この機では試せないことを実測しました(Homebrew に formula が無く、
コンテナ実行環境も無い)。ubuntu CI だけに入れる案は採りませんでした
——手元で再現できない欠陥を CI 往復で追うことになるためです。 - annotated tag への署名(v0.6.21 も署名なしです)。
- property-based 試験が見ているのは 1 つの性質だけです
(「新だけが通す値が実在するなら BUMP」)。値は schema 対から作りますが、
**作れない値があれば広がりを見逃します。**探索が働いていることは対照 5 つで
確かめていますが、値の網羅性そのものは示していません。 - 「無いと言ったのに在る」の検査は npm script 名を含む行しか拾いません。
広げるかを検討し、現時点で偽が 0 件だったので広げていません
(強い否定 9 件・うち検査できる 4 件はすべて本当に「無い」)。
数えたもの
| v0.6.20 | v0.6.21 | |
|---|---|---|
| 単体テスト | 1376 | 1410(skip 0 / 失敗 0) |
| 検算ツール | toolVersion 20 |
変わらず 20 |
| CLI 結果契約 | schemaVersion 2 |
変わらず 2 |
| 止め方の名前(catalog) | 82 種類 | 変わらず 82 |
| 出力の基準(経路) | 10 | 変わらず 10(全件 byte 一致) |
検証対象(validate:profiles) |
14 | 14 |
| schema | 25 | 25 |
| 記録された入力 | 32 | 32 |
| 区間 / event | TRS 23/32・TRRS 30/36 | 変わらず |
変わっていないもの
- **モデルの数値。**接点位置も走査結果も同じです。
verifiedPhysicalはfalse。実測記録は依然 0 件です。toolVersion20 / CLI 結果契約schemaVersion2 / catalog 82 種類。- 止め方の名前の一覧・
archivePolicy・status・終了コードの意味。