Skip to content

feat: シリーズで誰も発行していなかった effect: id を Edge が発行する - #419

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

01rabbit merged 1 commit into
mainfrom
claude/zealous-edison-k5vt5j

Conversation

@01rabbit

Copy link
Copy Markdown
Owner

Fabric#15 の effect_contracts は effect: 型 id を鍵にします。シリーズ全体を実測したところ、どのリポジトリもそれを発行していませんでした。

そのため EffectObservation と OutcomeObservationEnvelope は、どの製品が採用しても生産者が存在しえませんでした。materializer である Azazel-Deception は、観測する対象を持てませんでした(Azazel-Deception#46)。

連鎖の先頭はここに属します。DefensiveEffectRef は authority_class = producer_decision_ref を持ち、Edge の決定論アービタがシリーズ唯一の決定権限です。決定していない製品はそのクラスを名乗れません。

py/azazel_edge/outcome/effect_export.py が、アービタが既に作った ActionExecutionReceipt を横断参照として描画します。決定も選択も強制もしません。決定論経路はこのモジュールを import しません(テストで固定)。

埋めずに拒否する3件

拒否 理由
lease の無いアクション Fabric は expires_at を要求し、Edge の receipt では省略可能。期限を捏造すれば、Edge が時限を切っていない effect について「切った」と報告するレコードができる
ROUTE_CHANGE EffectClass に対応値なし。この enum は意図的に小さく、「2製品が同じ effect について話せるように名付ける」ものであって網羅しない。近い値に寄せれば別の effect を記述する
UNKNOWN Fabric が未知を observe_only に落とすのは消費者が fail-safe になるため。生産者が同じことをすれば、識別できなかった mechanism について observe_only を主張することになる

scope は不透明なハンドルとして渡す

Fabric はここに opaque typed ref を要求し、理由も述べています — provider コマンドであってはならず、受信側が作用すべきアドレスであってもならない。

Edge の scope は match フィールドの map なので、その digest を publish します。map は Edge ローカルに留まり、渡るのは Edge が解決でき受信側が作用できないハンドルです。

effect id は execution を同定し、mechanism を同定しない

observed mechanism が後で訂正されたとき id が動けば、それまでに行われた観測がすべて孤立します。 id の入力は decision / action / execution の3つだけであることを直接固定しました。

既存テストの誤検知を1件直した

test_nothing_on_the_deterministic_path_imports_this_export_surface はファイル本文の部分一致で shared_export の importer を探していました。本モジュールが docstring で shared_export を引用した時点で誤検知します。

コメントで壊れる境界検査は、コメントを消すことで回避されます。 import を構文解析する形に直しました。

既存の失敗12件について

main 上でも同じ12件が失敗します。本 PR は1件も増やしていません(main と branch の FAILED 集合が完全一致することを実測)。

原因は py/azazel_edge/bhusa_status.py が gh(GitHub CLI)を subprocess で呼び、未インストール環境で FileNotFoundError が伝播することです。--skip-github フラグは存在しますが既定経路が劣化しません。 本 PR の範囲外なので触っていません。

検証

tests/test_outcome_effect_export.py   18 passed
tests 全体                            12 failed, 808 passed  (12 は上記の既存失敗)
変異 結果
lease 不在時に期限を捏造 CAUGHT
ROUTE_CHANGE を近い class に割り当てる CAUGHT
UNKNOWN を observe_only に落とす CAUGHT (3)
scope をそのまま載せる CAUGHT
effect_id が mechanism を含む 最初は生存 → 下記
2つの mechanism が同じ class を名乗る CAUGHT
authority_class を下げる CAUGHT

生存した変異は、テストの主張が docstring の主張より狭かったためです。「mechanism 引数を変えて比較する」は引数を変えており、receipt のフィールドが id に紛れ込んでも通ります。id の入力が3つだけであることを直接固定して捕捉しました。

🤖 Generated with Claude Code

https://claude.ai/code/session_01SsboPSj6GyJju6mTXwHHjq


Generated by Claude Code

Fabric#15 の effect_contracts は effect: 型 id を鍵にする。シリーズ全体を
実測したところ、**どのリポジトリもそれを発行していなかった。** そのため
EffectObservation と OutcomeObservationEnvelope は、どの製品が採用しても
生産者が存在しえなかった。materializer である Azazel-Deception は、
観測する対象を持てなかった(Azazel-Deception#46)。

連鎖の先頭はここに属する。DefensiveEffectRef は
authority_class = producer_decision_ref を持ち、Edge の決定論アービタが
シリーズ唯一の決定権限である。決定していない製品はそのクラスを名乗れない。

py/azazel_edge/outcome/effect_export.py が、アービタが既に作った
ActionExecutionReceipt を横断参照として描画する。決定も選択も強制もしない。
決定論経路はこのモジュールを import しない(テストで固定)。

## 埋めずに拒否する3件

**lease の無いアクション。** Fabric は expires_at を要求し、Edge の receipt
では省略可能である。期限を捏造すれば、Edge が時限を切っていない effect に
ついて「切った」と報告するレコードができる。

**ROUTE_CHANGE。** EffectClass に対応する値が無い。この enum は意図的に
小さく、「2製品が同じ effect について話せるように名付ける」ものであって
製品が作れる effect を網羅しない。近い値に寄せれば別の effect を記述する。

**UNKNOWN。** Fabric が未知を observe_only に落とすのは**消費者**が
fail-safe になるためである。生産者が同じことをすれば、識別できなかった
mechanism について observe_only を**主張**することになる。

## scope は不透明なハンドルとして渡す

Fabric はここに opaque typed ref を要求し、理由も述べている — provider
コマンドであってはならず、受信側が作用すべきアドレスであってもならない。
Edge の scope は match フィールドの map なので、その digest を publish する。
map は Edge ローカルに留まり、渡るのは Edge が解決でき受信側が作用できない
ハンドルである。

## effect id は execution を同定し、mechanism を同定しない

observed mechanism が後で訂正されたとき id が動けば、それまでに行われた
観測がすべて孤立する。id の入力は decision / action / execution の3つだけで
あることを直接固定した。

## 既存テストの誤検知を1件直した

test_nothing_on_the_deterministic_path_imports_this_export_surface は
ファイル本文の部分一致で shared_export の importer を探していた。本モジュール
が docstring で shared_export を引用した時点で誤検知した。**コメントで壊れる
境界検査は、コメントを消すことで回避される。** import を構文解析する形に
直した。

## 既存の失敗12件について

main 上でも同じ12件が失敗する。**本 PR は1件も増やしていない**(main と
branch の FAILED 集合が完全一致することを実測)。原因は bhusa_status.py が
gh(GitHub CLI)を subprocess で呼び、未インストール環境で FileNotFoundError
が伝播すること。--skip-github フラグは存在するが既定経路が劣化しない。
本 PR の範囲外なので触っていない。

## 検証

tests/test_outcome_effect_export.py 18 passed
tests 全体: 12 failed, 808 passed(12 は上記の既存失敗)

| 変異 | 結果 |
|---|---|
| lease 不在時に期限を捏造 | CAUGHT |
| ROUTE_CHANGE を近い class に割り当てる | CAUGHT |
| UNKNOWN を observe_only に落とす | CAUGHT (3) |
| scope をそのまま載せる | CAUGHT |
| effect_id が mechanism を含む | **最初は生存** → 入力3件を直接固定して捕捉 |
| 2つの mechanism が同じ class を名乗る | CAUGHT |
| authority_class を下げる | CAUGHT |

Refs 01rabbit/Azazel-Deception#46, 01rabbit/Azazel-Fabric#49

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SsboPSj6GyJju6mTXwHHjq
@01rabbit
01rabbit marked this pull request as ready for review September 20, 2026 12:10
@01rabbit
01rabbit merged commit f130286 into main Sep 20, 2026
5 checks passed
01rabbit added a commit to 01rabbit/Azazel-Deception that referenced this pull request Sep 20, 2026
F-05。effect_contracts は生産者2つ(Deception の PresentedTerrainRef、
Edge の DefensiveEffectRef)を持ちながら**消費者が0**だった。契約は一度も
読まれておらず、相互運用は未知のままだった。R1c ゲートが問うているのは
「シリアライズできるか」ではなく「受け取った側と食い違わないか」であり、
**最初の読み取りこそが食い違いの表面化する場所**である。

runtime/effect_projection.py が Edge の DefensiveEffectRef を受理し、
それに対する EffectObservation を出す。これは同時に、本 repo が
「生産者が存在しえない」と記録していた穴を塞ぐ: 観測は effect: 型 id を
鍵にするが、シリーズの誰もそれを発行していなかった。Azazel-Edge#419 が
発行するようになった。

## 拒否は教義であって好みではない

AZ-06 は Edge が承認した環境を実体化する。よって:

**decision を伴わない effect を拒否する。** advisory_inference は助言、
planned_shadow は検討であって承認ではない。実体化すれば AZ-06 が
「示唆を環境に変える段」になる。まさに持っていない権限である。

**redirection 以外を拒否する。** network_isolation や notify_only の背後に
AZ-06 が提示するものは無い。producer_evidence が redirection 機構のみを
受理するのと同じ理由である。

**自分が作った effect を拒否する。** 自分の生産物が指示として戻ってくれば、
authority_class が何であれ、それは自分が決定していることになる。

## 観測は trace を呼び出し側から受け取らない

effect から取る。呼び出し側が trace を名乗れる観測は、無関係なインシデントに
紐付けられる。しかも連鎖検査は通る — 両半分が互いに一致し、他の何とも
一致しないからである。

authority_class も status から導出する。Fabric は live な status に
active_materialized を要求し、それ以外では禁止するので、呼び出し側が渡せる
値は「一致するか拒否されるか」しかない。導出すれば、正しい値が1つしかない
フィールドが消える。

## 連鎖検査は到達不能な backstop ではない — 測定した

現在の構成では失敗しえない(trace も effect_ref も effect から読む)。
しかし trace_id を呼び出し側パラメータにする変異を当てると、
**この呼び出しがある状態で11件、無い状態で3件**が失敗する。
8件はこの検査が捕まえている。trace を交渉不能にしているのはこれである。
コメントを測定結果に合わせた。

## 検証

590 passed, 8 skipped (574 → 590)

| 変異 | 結果 |
|---|---|
| decision を伴わない effect を受け入れる | CAUGHT |
| redirection 以外も materialize する | CAUGHT |
| 自分が作った effect を受け入れる | CAUGHT |
| 観測の trace を呼び出し側から受け取る | CAUGHT (11) |
| live でない status に active_materialized を付ける | CAUGHT (4) |
| 期限切れの live 観測を許す | CAUGHT |
| 観測 id が status と時刻を無視する | CAUGHT |
| 連鎖検査を外す | 単独では無変化(上記のとおり構成上失敗しえない) |

役目を終えたガード
(test_nothing_in_the_series_mints_an_effect_id_so_observations_are_blocked)は
削除せず、次に重要な不変条件 — AZ-06 の観測が名指す effect に実際に連鎖する
こと — へ置き換えた。

Refs 01rabbit/Azazel-Edge#419, 01rabbit/Azazel-Fabric#15


Claude-Session: https://claude.ai/code/session_01SsboPSj6GyJju6mTXwHHjq

Co-authored-by: Claude <noreply@anthropic.com>
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