Skip to content

feat: Fabric pin を v0.9.0rc4 へ上げ、局所拒否を生きたガードに戻す - #423

Merged
01rabbit merged 2 commits into
mainfrom
claude/zealous-edison-k5vt5j
Sep 20, 2026
Merged

01rabbit merged 2 commits into
mainfrom
claude/zealous-edison-k5vt5j

Conversation

@01rabbit

Copy link
Copy Markdown
Owner

⚠ CI はタグが直るまで赤です

v0.9.0rc4 タグが誤ったコミット(3a9543a = PR #53、version.py が 0.9.0rc4.dev0 で manifest も署名も不在)を指しております。正しい対象は Azazel-Fabric の 0c85091 です。タグ訂正後に CI を再実行すれば緑になります。ローカルでは 0c85091 を導入して全緑を確認済みです(866 passed / 4 skipped)。

何を

Fabric pin を rc2 → rc4(rc3 を飛ばす)。

  • rc4 — EffectObservation が名乗れる権限クラスを 2 つに限定(Azazel-Fabric#52)。非加法的。Edge は effect_observation_reader が同じ 4 クラスを既に局所拒否していたため実運用への影響なし
  • rc3 — 型付き参照の本体にコロンを許す。Edge が発行する edge:nft:1 は rc2 の文法では型付きスロットすべてに拒否されていたため、rc3 も Edge にとって任意ではない

発見 — 局所拒否はどちらの構成でも走っていなかった

マスターのご指示は「Edge 側の局所拒否は維持」でした。維持しようとして、それが到達不能であることが判明しました。

observation = EffectObservation(**dict(payload))   # ← rc4 では Fabric が先に拒否

if observation.authority_class.value not in OBSERVER_AUTHORITY_CLASSES:
    raise ObservationRejected(...)                 # ← 到達しない

Fabric 不在時も read_effect_observation が早期 return するため、どちらの構成でも一度も走らないガードでした。

モデル構築の前へ移しました。これで:

  • 操作者は pydantic のダンプではなく Edge の文章を読む
  • AuthorityClass の次の変更が今回と同じ向きに狭まる保証はない。一度狭まったことは、次も狭まる理由にならない

未知の権限値は最弱クラスへ強制せず、そのまま拒否理由に出します。強制は Fabric 側では正しい(未知の入力が昇格してはならない)ものの、この境界では payload が名乗っていないクラスを理由に挙げることになり、操作者が存在しない記録を探しに行きます。

テスト

「契約はこれを通す」と断言していた 4 件(assert EffectObservation(**payload))を反転させました。ギャップを記録する仕事を終えたテストとして正しい結末です。その断言こそが Fabric#52 になった計測でした。

追加:

テスト 何を固定するか
..._agree_with_fabrics_exactly 局所集合 == Fabric の OBSERVABLE_AUTHORITY_CLASSES。従来の「真部分集合」では、Edge 側が要素を失っても通る
..._still_partition_the_enum_between_them AuthorityClass から列挙した補集合を Edge が拒否する。将来 Fabric に追加されたクラスが見える
..._reaches_the_operator_before_fabrics_does 順序が load-bearing であること
..._refused_as_what_it_said 未知値を最弱クラスに強制しないこと

OBSERVER_AUTHORITY_CLASSES は import せず literal のままです。これは意図的な重複であり、意図的な重複とはテストが下にあるもののことです。Azazel-Edge#413 が見つけた「維持の偶然」——Fabric のモデルとフィールド単位で一致する局所 dataclass を何も比較していなかった——との違いはそこだけです。

pydantic を直接 import しない

ValidationError を import したところ test_runtime_dependency_contract が発火しました。pydantic は Fabric extra 経由でしか入らず Edge は宣言していません。依存契約テストの許可リストを緩めるのは方向が逆ですので、例外クラスを Fabric の実挙動から導出しました。導出時の else 分岐も生きたガードです(Fabric が空 payload を受理するようになれば、この file の拒否テスト全体が無意味化する前に赤くなります)。

変異試験

変異 結果
E1 局所拒否をモデル構築の後に戻す(元の到達不能状態) 6 件失敗
E2 局所集合から active_materialized を落とす 6 件失敗
E3 局所集合に producer_decision_ref を足す 3 件失敗
E4 未知の権限値を stale_or_unknown に強制 検出

検証(ローカル、Fabric 0c85091 導入下)

tests 全体   866 passed, 4 skipped, 70 subtests passed
compileall   OK

🤖 Generated with Claude Code

https://claude.ai/code/session_01SsboPSj6GyJju6mTXwHHjq


Generated by Claude Code

Azazel-Fabric#52 は EffectObservation が名乗れる権限クラスを
observed_fact と active_materialized の2つに限定した。非加法的変更で
あり、rc3 で valid だった入力を rc4 は拒否する。Edge は
effect_observation_reader が同じ4クラスを既に局所拒否していたため
実運用への影響はない。

pin は rc3 を飛ばして rc2 -> rc4 とする。rc3 は型付き参照の本体に
コロンを許す変更で、Edge が発行する edge:nft:1 は rc2 の文法では
型付きスロットすべてに拒否されていた。rc3 も Edge にとって任意ではなく、
同じ移動で入る。

局所拒否が到達不能になっていたため、モデル構築の前に移した。
EffectObservation の構築が先に走るため rc4 導入後は Fabric が先に拒否し、
Fabric 不在時は read_effect_observation が早期 return する。つまり
どちらの構成でも一度も走らないガードだった。前に移すことで、
操作者は pydantic のダンプではなく Edge の文章を読む。また
AuthorityClass の次の変更が今回と同じ向きに狭まる保証はない。

未知の権限値は最弱クラスへ強制せず、そのまま拒否理由に出す。強制は
Fabric 側では正しい(未知の入力が昇格してはならない)が、この境界では
payload が名乗っていないクラスを理由に挙げることになり、操作者が
存在しない記録を探しに行く。

テスト: 「契約はこれを通す」と断言していた4件は反転させた。ギャップを
記録する仕事を終えたテストとして正しい結末である。局所集合と Fabric の
OBSERVABLE_AUTHORITY_CLASSES の一致、AuthorityClass からの列挙による
補集合の拒否、順序が load-bearing であることを固定するテストを追加した。

pydantic は直接 import しない。Fabric extra 経由でしか入らず Edge は
宣言していないため、例外クラスは Fabric の実挙動から導出する。
依存契約テストの許可リストを緩めるのは方向が逆である。

tests 全体 866 passed / 4 skipped、compileall OK。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SsboPSj6GyJju6mTXwHHjq
pin は主張である。何が入ったかは別の事実であり、この差を見るものが
無かった。

計測から書いた。v0.9.0rc4 への移行準備中にタグが2度誤ったコミットで
切られ、2度目は本リポジトリの CI がそれを導入して成功を報告した。

  Running command git checkout -q ca05d5ab2b8e3f7c959c3966d7c04b1d9412a327
  Created wheel for azazel-fabric: filename=azazel_fabric-0.9.0rc4.dev0-...
  Successfully installed ... azazel-fabric-0.9.0rc4.dev0 ...
  866 passed, 4 skipped

タグが変更のマージコミットを指していたため、コードは正しく、挙動の
テストはすべて通った。誤っていたのはリリースの側である。版数
0.9.0rc4.dev0、digest manifest 無し、署名無し。開発版がリリースタグを
着て入り、Edge にはそれを見る手段が無かった。

requirements/fabric.txt は「an exact tagged release, never a branch and
never a development commit」と方針を述べ、
test_defensive_state_vocabulary.py はその「ファイルがそう書いているか」
を検査している。どちらも「何が届いたか」を問わない。

等価比較にしてある。0.9.0rc4.dev0 は 0.9.0rc4 で始まるため、
startswith や前方一致で書けば、このテストが拒否すべき当のビルドを
素通りさせる。.dev0 こそが差分である。

誤ったビルド(3a9543a)を実際に導入して2本とも赤くなることを確認した。

tests 全体 869 passed / 4 skipped、compileall OK。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SsboPSj6GyJju6mTXwHHjq

Copy link
Copy Markdown
Owner Author

タグは訂正済み、CI の赤の原因は解消しました

v0.9.0rc4 が正しいリリースコミット 0c85091 を指すようになりました。タグ自身の checkout で検証済みです。

__version__ = "0.9.0rc4"
manifest:  存在
signature: 存在
release/v0.9.0rc4.digest.json matches: sha256:b02ee97b...101d
v0.9.0rc4.digest.json signed by: release-owner

追加のガード — この PR で直接扱うべき欠落が 1 つ判明しました

この PR の最初の CI 実行が、誤ったタグを導入して成功を報告しました。推測ではなく、ジョブログの実測です。

Running command git checkout -q ca05d5ab2b8e3f7c959c3966d7c04b1d9412a327
Created wheel for azazel-fabric: filename=azazel_fabric-0.9.0rc4.dev0-py3-none-any.whl
Successfully installed ... azazel-fabric-0.9.0rc4.dev0 ...
866 passed, 4 skipped

誤ったタグは変更のマージコミットを指していたため、コードは正しく、挙動のテストはすべて通りました。誤っていたのはリリースの側です — 版数 0.9.0rc4.dev0、digest manifest 無し、署名無し。開発版がリリースタグを着て入り、Edge にはそれを見る手段がありませんでした。

requirements/fabric.txt は「an exact tagged release, never a branch and never a development commit」と方針を述べ、test_defensive_state_vocabulary.py はそのファイルがそう書いているかを検査しています。どちらも「何が届いたか」を問うていません。

tests/test_fabric_pin_integrity.py を追加しました(3 件)。

等価比較である理由

0.9.0rc4.dev0 は 0.9.0rc4 で始まります。startswith や前方一致で書けば、このテストが拒否すべき当のビルドを素通りさせます。.dev0 こそが差分です。

                今回 CI に入った 0.9.0rc4.dev0 に対して
  equality  : FAIL ← 捕捉
  dev suffix: FAIL ← 捕捉
  startswith: PASS ← 素通り

実物で確認しました

誤ったビルド(3a9543a)を実際に導入し、2 本とも赤くなることを確認しております。

導入: 0.9.0rc4.dev0
FAILED tests/test_fabric_pin_integrity.py::test_the_installed_fabric_is_the_tag_this_repository_pinned
FAILED tests/test_fabric_pin_integrity.py::test_a_development_build_is_refused_however_close_the_version_looks

正しいタグに戻すと 3 件とも通ります。

検証(ローカル、@v0.9.0rc4 導入下)

導入: 0.9.0rc4
tests 全体   869 passed, 4 skipped, 70 subtests passed
compileall   OK

Generated by Claude Code

@01rabbit
01rabbit marked this pull request as ready for review September 20, 2026 14:08
@01rabbit
01rabbit merged commit 873f760 into main Sep 20, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants