Ethotrace
Ethotrace は Ruby 向けの動的型検査・シグネチャ解析システム。公称型(型名)ではなく、 テスト実行中のメソッド呼び出しを観測して「振る舞い(プロトコル)」「伝播例外」 「実行環境要件(Requirements)」を三チャネルで記録する。
設計思想は Effect-TS の Effect<Success, Error, Requirements> モデルに着想を得ている。
詳細な設計は docs/ethotrace-design-handoff-v2.md、
gem 間の安定契約である JSONL スキーマは docs/schema.md を参照。
観測結果は observed contract(観測された下限) であり、テストカバレッジに依存する。 未実行パスの規約は含まれない。これは欠陥ではなく仕様である。
モノレポ構成
単一リポジトリに複数 gem を置くモノレポ構成(段階的に追加予定):
| gem | 役割 |
|---|---|
gems/ethotrace |
core: 観測エンジン + stdlib アダプタ + JSONL ライター + マージ CLI |
gems/ethotrace-rspec |
RSpec ライフサイクル接続 |
gems/ethotrace-minitest |
Minitest 版(予定) |
gems/ethotrace-rails |
Railtie / ActiveSupport::Notifications / Zeitwerk 連携(予定) |
gems/ethotrace-mcp |
観測結果を提供する MCP サーバ(観測プロセスと完全分離) |
gems/ethotrace-rbs |
RBS interface への投影(予定) |
実装状況
| マイルストーン | 内容 | 状態 |
|---|---|---|
| M0 | スキャフォールド + スキーマ v1 確定 | ✅ |
| M1 | prepend ラッパー + Tracker + CallContext + 再入ガード + JSONL ライター(Success / Error チャネル) | ✅ |
| M2 | アダプタ API + stdlib アダプタ(ENV/Time/Random/IO/Process)+ Requirements 帰属 + エフェクトスパン | ✅ |
| M3 | TracePoint エンジン + 引数プロトコル観測 | ✅ |
| M4 | ethotrace-rspec(スイートライフサイクル接続)+ マージ CLI(ethotrace merge) |
✅ |
| M5 | 自己適用(self-hosting)+ 隔離戦略(NullIsolation / BoxIsolation)+ collector/probe 分離 |
✅ |
| M6 | ethotrace-mcp(自己観測データを参照する MCP サーバ) |
✅ |
| M7 | ethotrace-rails(Railtie / Notifications / Zeitwerk) |
予定 |
| M8 | ethotrace-rbs(protocol → RBS interface 投影) |
予定 |
ロードマップは設計資料 v2 で改定済み: MCP を Rails より先に作る。M5 で自己適用を達成し、 M6 の MCP 経由で以降の開発を自己観測データで支援するブートストラップループに入る(§9)。
core の使い方は gems/ethotrace/README.md を参照。
使い方(RSpec)
ethotrace-rspec をテストに組み込むと、スイート開始/終了に観測セッションが接続され、
テストワーカーごとに tmp/ethotrace/<session>.jsonl が書き出される。spec_helper.rb で:
require "ethotrace/rspec"
Ethotrace::RSpec.setup do |config|
config.observe Order # Order 自身のインスタンスメソッド全部
config.observe Tax, methods: %i[rate] # 一部メソッドだけを指定
# config.options[:trace_c_call] = true # C メソッドもプロトコルに含める(高コスト)
end
並列テスト(parallel_tests)ではワーカーごとに TEST_ENV_NUMBER と PID から
セッション ID を導出し(例: rspec-w2-pid4242)、別ファイルへ書き分ける。
マージ(ethotrace merge)
並列ワーカーが吐いた複数の JSONL を 1 つへ統合する。(owner, name, kind) をキーに、
プロトコル・例外・Requirements を和集合、samples を加算する(→ docs/schema.md §7)。
bundle exec ethotrace merge tmp/ethotrace/*.jsonl -o ethotrace/observations.jsonl
出力は method_observation 形を 1 行 1 レコードで保つため再マージ可能。
-o を省略すると標準出力へ純粋な JSONL を流す(進捗・サマリは標準エラーへ)。
マージ結果は ethotrace view でそのまま閲覧できる。
MCP サーバ(ethotrace-mcp)
マージ済みの観測結果を MCP サーバ(stdio / JSON-RPC) として公開し、AI エージェント (第一のユーザーは Ethotrace を開発する Claude Code 自身)が各メソッドの規約を参照しながら 開発を進められるようにする。観測プロセスとは完全分離したデータ消費者で、計装は一切起動 しない(設計資料 §9 / M6)。
# 既定で ethotrace/observations.jsonl を読み、stdio で待ち受ける
bundle exec ethotrace-mcp
bundle exec ethotrace-mcp path/to/observations.jsonl # 読むストアを明示
Claude Code へは .mcp.json.example を .mcp.json にコピーするとプロジェクトスコープの
MCP サーバとして登録される。公開ツール:
| ツール | 返すもの |
|---|---|
lookup_method |
1 メソッドの全規約(プロトコル・戻り値・例外・Requirements) |
list_methods |
観測された全メソッド |
pure_methods |
Requirements が空(R=∅ = 純粋の容疑) |
flaky_suspects |
非決定性 Requirements(time.read / random.read 等)に接触 |
requiring |
特定エフェクト語彙(db.query / env.read 等)に接触 |
methods_raising / exceptions_of |
例外をエスケープさせうるメソッド / その例外クラス |
param_protocol |
指定引数に観測されたプロトコル |
返る規約は observed contract(テストで実行されたパスの下限)。詳しい自己観測ループの手順は
CLAUDE.md を参照。
自己適用(self-hosting)と隔離戦略
Ethotrace は 観測の設置先を差し替え可能な隔離戦略(Ethotrace::Isolation)として持つ。
probe(prepend フック)を「どの空間で」走らせるかだけを戦略が決め、collector(記録・帰属・
JSONL)と TracePoint は常に root 側に置く。
| 戦略 | 条件 | 用途 |
|---|---|---|
Isolation::Null |
既定 | 通常の解析(probe = collector 同一空間) |
Isolation::Box |
Ruby >= 4.0 + RUBY_BOX=1 |
自己適用・モンキーパッチの激しい対象 |
Isolation::Box は子 Ruby::Box(Ruby 4.0
experimental)に Ethotrace を新規ロードし、観測対象を root と別実体に隔離する。これにより
「計装機構そのものが計装対象になる」自己言及汚染が構造的に解ける。観測イベントは box 内 probe
から root の Collector へ橋渡しされる。加えて記録の活性パスは名前ベースの deny-list
(Wrapper::SELF_DENY_LIST)で二重に保護する。
# 自己適用デモ: Ethotrace で Ethotrace 自身のメソッドを観測する(main box から起動)
RUBY_BOX=1 ruby gems/ethotrace/script/self_hosting.rb
Ruby::Box の検証結果(実体分離・越境観測・系譜依存など E1〜E6)は
docs/box-semantics.md に、設計の詳細は設計資料 §4.8 に記す。
Ruby::Box は experimental かつ main box からの起動を要するため、自己適用の検証は上記の
スタンドアロンハーネス(素の RUBY_BOX=1 ruby)で行う(RUBY_BOX=1 下では bundler/rspec が
stdlib autoload の問題で動かない)。Rails アダプタ + BoxIsolation の併用は当面サポート外。
開発
Ruby >= 3.2 が必要。
bundle install # 依存解決(ルートの Gemfile が全 gem を束ねる)
bundle exec rake # 全 gem の spec + RuboCop
bundle exec rake spec # テストのみ
bundle exec rake rubocop
ライセンス
MIT
No comments yet
Be the first to share your take.