プログラミング
Show HN: RealDiff – プルリクエストの実行時動作差分検出ツール(6言語対応)
Show HN: RealDiff – runtime behavior diffing for pull requests (six languages) (github.com)
要約
RealDiffは、プルリクエストにおける実行時の動作変更を検出するツールです。通常のソースコード差分では見逃されるような、リファクタリングなどが意図せず引き起こした予期せぬ動作変更を特定します。複数のプログラミング言語に対応し、コードの品質向上とバグの早期発見に貢献します。
全文翻訳
RealDiffは、通常のソースレビューでは見逃される実行時動作の変更を検出します。2つのGitリビジョンをビルドし、テストを実行して、3回のベース実行からノイズのベースラインを学習し、各コールツリーで最初に変更された動作を報告します。ソースコードの差分は、何が編集されたかを教えてくれます。RealDiffは、編集が何をしたのか、プルリクエストが触れていないファイルへの影響も含めて教えてくれます。
なぜこれを使うのか?
無害に見えるリファクタリングが、編集されたファイルから遠く離れた動作を変更することがあります。
List.Sort は安定ソートではありません。OrderBy は安定ソートです。
付属のデモでは、その1行のインフラストラクチャの変更が、編集されていない価格設定エンジンに影響を与えます。
RealDiff: この差分の外側に1つの動作ギャップがあります。
DiscountEngine.SelectDiscount は "CLEARANCE_40" を返していましたが、現在は "SEASONAL_15" を返します。
CheckoutTotals.Compute は 60 を返していましたが、現在は 85 を返します。
実行された3つのテストのうち2つは、変更をアサートしていませんでした。
編集されたヘルパーは Infrastructure.Collections にあり、観測された影響は Commerce.Pricing にあります。
付属のデモを以下で実行するか、メンテナンスされている公開.NETデモプルリクエストとその正常にホストされた実行を確認してください。
5分間の.NETデモ
この.NETデモの前提条件:
Git、.NET 8 SDK、およびPowerShell 7。
Java分析には、JDKとリポジトリのMaven/Gradleラッパーまたは対応するシステムツールも必要です。
Node分析には、Node.jsとロックファイルによって選択されたパッケージマネージャーが必要です。
```bash
git clone https://github.com/issacnitin/RealDiff.git
cd RealDiff
dotnet build RealDiff.sln -c Release
pwsh -File tools/verify-diff.ps1 -Mutate -Change sort
```
この証明は、一時的な変更提案ツリーを作成し、SortingExtensions.csのみを変更し、ベースを2回実行し、変更を1回実行して、findings.jsonに書き込みます。
検証内容:
編集されたファイルはゼロのトレースメンバーに寄与します。
フロンティアは Commerce.Pricing.DiscountEngine.SelectDiscount で、編集されていないプロジェクトです。
アサーションが反応せずに2つのコールサイトが変更されました。
5つの分岐キーが3つのフロンティアノードに収束します。
等しい優先度の選択は、新しいプロセス間で決定的です。
すべてのメンテナンスされているデモモードを実行します:
```bash
pwsh -File tools/verify-demo-fixtures.ps1
```
これは、ソートの安定性、リトライポリシー、および構成解析をカバーします。
公開されている実行可能ファイルは、引数ルーティング、リポジトリ構成のロード、および検出を管理する薄いRustランチャーです。これは、参照解決、ビルド、キャッシュ、インストルメンテーション、および投稿のために、兄弟の自己完結型マネージドコンポーネントを開始します。
アーキテクチャは、言語ニュートラルなトレース契約、ランタイムごとのトレーサー、および単一パスストリーミングRust差分、フロンティア、および検出エンジンで構成されます。
```mermaid
flowchart LR
L[Rust argv, config, detect] --> O[Managed orchestration]
O --> D
O --> J
O --> N
O --> G
O --> R
O --> P
D[.NET / Cecil] --> T[realdiff.trace/1]
J[Java / javaagent + ASM] --> T
N[Node / CJS + ESM + Babel] --> T
G[Go / stable AST rewrite] --> T
R[Rust / stable syn rewrite cache] --> T
P[Python / PEP 669 sys.monitoring] --> T
T --> E[Rust matching, noise, frontier, and findings]
E --> F[findings.json]
F --> P[GitHub, Azure DevOps, MCP]
```
TRACE-FORMAT.mdのロードは、トレーサーとエンジンの間の契約です。
メンテナンスされている.NET、Java、Node、Go、Rust、およびPythonゲートは、同じ準拠ルールを適用します。
同一のメソッドセット、キーごとのイベント数とエントリ順序、ソースのトリップワイヤー、ダイジェスト証明、および非空実行からのゼロエンジン逸脱。
ステータス: 初期プレビュー。
統一CLIは、従来のルートマーカーから.NET、Maven/Gradle Java、npm/pnpm/Yarn/Bun Node、Goモジュール、Cargo Rust、およびPython 3.12+リポジトリを検出します。
サポートされている言語
| Language | Instrumentation | Test/source integration | Current limits |
|---|---|---|---|
| .NET 8 | Mono.Cecil build-time IL weaving | xUnit and portable PDBs | Properties, events, and operators are policy exclusions. Type initializers are structurally unobservable because hooks run under the CLR type-initialization lock and can deadlock startup. |
| Java | java.lang.instrument agent with ASM | Maven/Gradle, JUnit/TestNG annotations, inferred or configured source roots | Collection shape rules require java.module access. Class initializers are structurally unobservable because hooks run under the JVM class-initialization lock and can deadlock startup. |
| Node / TypeScript | CommonJS require hook and ESM loader with Babel | npm, pnpm, Yarn Classic/Berry, Bun, direct JavaScript locations, TypeScript source maps, Jest/Vitest adapters | Exactly one supported lockfile is required; workers are out of scope; generators and unsupported callables are skipped. |
| Go | Stable module-aware AST rewriting into a build cache | go test, original .go parser positions | Dynamic interface/function boundaries and unrewritten goroutine boundaries are explicit skips. |
| Rust | Stable syn/quote rewriting into a SHA-256 build cache | cargo test, structural #[test] roots, original .rs parser positions | Macro expansions, extern/const callables, unions, trait objects, and dependency-owned values are structurally unreachable because stable source rewriting cannot enter expanded/compiler-owned code or inject readers into dependency source. The MIR prototype emitted zero runtime events. |
| Python 3.12+ | PEP 669 sys.monitoring attached at process start; no build, bytecode weaving, or AST rewriting | pytest and unittest structural roots; co_filename/co_firstlineno; source AST inventory | Native/C callables are structurally unobservable because they have no Python frame for sys.monitoring; synthetic code without repository source and module/class setup bodies are explicit unsupported boundaries. Python 3.11 and older are refused; there is no sys.settrace fallback. |
Unresolved TypeScript source mapsはトレーサーの制限とは見なされません。RealDiffは元のパスを推測することを拒否し、ソースを未解決として記録し、誤ったファイルを主張する代わりに帰属の信頼性を低下させます。
すべてのトレーサーは同じプロセススコープのNDJSON契約と、調整されたカバレッジマニフェストを発行します。インストルメントされたと報告されたメンバーは発行可能でなければならず、すべてのモジュールは discovered = instrumented + skipped で、パッチ障害ゼロを満たす必要があります。
Pythonはコンパイル済みトレーサーとは異なります。注入するためのビルドコマンドがないためです。RealDiffはステージングされたsitecustomize.pyをPYTHONPATHにプリペンドし、ターゲットインポートの前にsys.monitoringをアタッチし、リポジトリのテストをそのまま実行します。
副作用のないASTパスは、カバレッジマニフェストのためだけにソースメンバーを在庫します。実行時イベントは、PEP 669からのみ生成されます。
ベーストレースキャッシュは健全性を保ちます。そのキーには、ステージングされたPythonトレーサーディレクトリのフィンガープリント、Pythonのメジャー.マイナー.マイクロ、有効なインクルード/エクスルードスコープ、および赤面構成が含まれます。
ソース解決のために、リポジトリの下にある実際のco_filenameとco_firstlinenoがdebugInfoです。存在しない/合成のファイル名はdebugInfoMissingです。正規化できない、または外部の実際のパスはunresolvedです。
PythonはgeneratedStateを必要としません。サスペンドされた呼び出し可能オブジェクトは元のコードオブジェクトを保持し、安全なdeclaringTypeのフォールバックがありません。
Pythonの値サポートは明示的です:
| Value support | Details |
|---|---|
| Confidence Shapes Exact | None, missing values, booleans, arbitrary integers, floats including canonical NaN and distinct -0.0, strings, bytes, and exact built-in list, tuple, dict, set, and frozenset; complete instance __dict__ state when no other state channel exists. |
| Partial | Properties, __slots__, __getattr__, overridden __getattribute__, container subclasses, unreadable fields, depth/breadth limits, and display truncation. |
読み取られない各領域は、counted <skipped:Python:...>, <error:Python:...>, <depth:...>, または <truncated> マーカーを発行します。
| Unsupported Shape | Details |
|---|---|
| Native/C callables | without Python code objects, dynamic/synthetic code without repository source, and execut |