bulldogger 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -0
  3. data/README.ja.md +113 -107
  4. data/README.md +114 -88
  5. data/docs/design-decisions.md +172 -0
  6. data/docs/evidence-schema.md +142 -55
  7. data/docs/maintenance.md +26 -9
  8. data/exe/bulldogger +66 -4
  9. data/lib/bulldogger/application_frames.rb +2 -2
  10. data/lib/bulldogger/capture.rb +15 -1
  11. data/lib/bulldogger/code_state.rb +40 -0
  12. data/lib/bulldogger/collector_environment.rb +16 -0
  13. data/lib/bulldogger/config.rb +3 -16
  14. data/lib/bulldogger/evidence.rb +8 -19
  15. data/lib/bulldogger/exec.rb +86 -0
  16. data/lib/bulldogger/exec_collector.rb +142 -0
  17. data/lib/bulldogger/execution_target.rb +112 -0
  18. data/lib/bulldogger/failure_output.rb +7 -19
  19. data/lib/bulldogger/flt.rb +54 -0
  20. data/lib/bulldogger/flt_collector.rb +186 -0
  21. data/lib/bulldogger/frames.rb +76 -0
  22. data/lib/bulldogger/frames_collector.rb +166 -0
  23. data/lib/bulldogger/instance.rb +12 -15
  24. data/lib/bulldogger/integrations/minitest.rb +43 -24
  25. data/lib/bulldogger/integrations/rspec.rb +26 -16
  26. data/lib/bulldogger/preflight.rb +79 -0
  27. data/lib/bulldogger/probe/bucket.rb +8 -7
  28. data/lib/bulldogger/probe/comparator.rb +3 -2
  29. data/lib/bulldogger/probe/method_stats.rb +1 -1
  30. data/lib/bulldogger/probe/raise_tracker.rb +3 -3
  31. data/lib/bulldogger/probe/session.rb +7 -5
  32. data/lib/bulldogger/probe.rb +5 -4
  33. data/lib/bulldogger/rerun_command.rb +48 -0
  34. data/lib/bulldogger/version.rb +1 -1
  35. data/lib/bulldogger.rb +10 -30
  36. data/skills/bulldogger/SKILL.md +123 -31
  37. data/skills/bulldogger/references/failure-evidence.md +2 -6
  38. data/skills/bulldogger/references/reexecution.md +149 -0
  39. metadata +15 -11
  40. data/docs/trace-schema.md +0 -123
  41. data/lib/bulldogger/record/session.rb +0 -343
  42. data/lib/bulldogger/record/sqlite_converter.rb +0 -86
  43. data/lib/bulldogger/record/writer.rb +0 -67
  44. data/lib/bulldogger/record.rb +0 -47
  45. data/lib/bulldogger/replay.rb +0 -123
  46. data/skills/bulldogger/references/record.md +0 -28
  47. data/skills/bulldogger/references/replay.md +0 -88
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5fd076d39e5f2dcaf2d18cd10c8863a406ec9cc06f37874bcf85e5e4dcb22020
4
- data.tar.gz: d966b9455420d78ac41a8cbc84354b3ae3d674bb89f419a1ba4890ba6b07d52b
3
+ metadata.gz: 72ea09764a4ece080b0430f55231aa772b451c37174604a2c6352077d36de9e2
4
+ data.tar.gz: d4e01f7ea18e1d7c6e5183dbe8bd7a6e800c4e6c11f0480933db7268d4429eb3
5
5
  SHA512:
6
- metadata.gz: f152d7d4d1c4d3be122dfaa37db861661102f89aac9eb0636e8c142b91853aceacab4132a0709454f1231adec64a327b32a3b94a759fce2d74102541fd130b1b
7
- data.tar.gz: 978f85bc4b6dd7bde8bd5c2f040780d612d5574ea322ca9148cac04faaac3d3cd670ce94fc84d7545d627804d780b45af040e9af71fbf293ec28aebe3947afc1
6
+ metadata.gz: 25cfda27f1f9255f254b581004346847fafd2397099a67b67bb244801ff50125d95e5dfc437a27f78ce4b110d6982a6194b7970f08cfab5be18dbab242e4e787
7
+ data.tar.gz: 22382a54852bd5bd0b0d062d041fb058d3b8a7ccd9da4f77378faf0d27afe4f27efdd8f36e3e3949ad78ac75c7ee4afece7b240958dec617db246dafef556a4f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,89 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### The re-run a failure needs is now an explicit verb, not an automatic one
6
+
7
+ `0.2.0` reached the value behind an assertion failure by re-running the one
8
+ failing test automatically, under full recording, in a child process. That
9
+ worked, but it re-ran on every qualifying failure whether or not anyone
10
+ asked, and it paid for a whole-run trace to answer one question. This release
11
+ replaces that automatic replay with re-execution the agent drives, one
12
+ question at a time, and retires the `record` verb along with it.
13
+
14
+ This removes `record`, automatic replay, and their evidence keys, so it is
15
+ a breaking change for a `0.2.0` user who read them. The failure snapshot and
16
+ `probe` are unchanged.
17
+
18
+ `0.3.0` carries the same work, but its release run stopped before publishing
19
+ when a fixture pinned an older minitest than the suite now uses. Its tag
20
+ stands at that commit and no gem was pushed, so `0.3.1` is the first
21
+ published release of the re-execution verbs, with the fixture pinned to the
22
+ minitest the suite loads.
23
+
24
+ - **Four re-execution verbs join `probe`.** `frames` runs a test in a fresh
25
+ process and indexes every call by a stable identifier, `fid`
26
+ (`path:method#k`, where `k` counts calls to that method inside the test
27
+ window). `preflight` runs a test twice in isolation and compares the two
28
+ application-frame sequences, so an agent can trust a `fid` from one run to
29
+ address the same call in the next. `flt` traces one selected frame: its
30
+ entry arguments and locals, each line's changed locals, and its return.
31
+ `exec` evaluates one statement at one addressed line visit inside a
32
+ selected frame, gated behind the `BULLDOGGER_EXEC=1` launch token the
33
+ launcher sets for the child process.
34
+ - **`record` and automatic replay retire.** Whole-run tracing measured 881x
35
+ to 2869x with debug.gem's recorder, and 34x to 51x with the shipped record
36
+ path; `frames` plus `flt` answer the same questions at re-execution cost,
37
+ paid only when invoked. `flt` and `exec` both refuse a target outside the
38
+ project's own source, naming the frames index, the gem source, and `probe`
39
+ as the alternatives.
40
+ - **The failure output's rerun line is the whole handoff.** `bulldogger
41
+ rerun:` prints the complete command for the failing test and seed; an
42
+ agent runs it under `frames` to reach a value whose producing method had
43
+ already returned. A green run still costs nothing extra, and re-execution
44
+ runs only when an agent invokes it.
45
+
46
+ ### Running bulldogger over its own suite
47
+
48
+ - **`Bulldogger::Instance`** separates the observing tool from the subject
49
+ under test. A single module singleton could not be both, because a suite's
50
+ own setup resets bulldogger for test isolation and destroyed any outer
51
+ observer. The module now delegates to a default instance, and
52
+ `Bulldogger::Minitest.instance=` and `Bulldogger::RSpec.instance=` accept
53
+ another one.
54
+ - `rake dogfood` covers the unit suite as well as acceptance.
55
+
56
+ ### Reaching the skill
57
+
58
+ - **`exe/bulldogger`** launches the re-execution verbs and answers two more
59
+ questions: `skill path` prints the location of the skill shipped with the
60
+ installed gem, and `version` prints the version.
61
+ - Evidence and probe evidence carry a `skill` key holding that path. The key
62
+ is omitted when the file is absent, because a path that does not resolve
63
+ costs a reader more than a missing one.
64
+ - The skill teaches what the files cannot: that an absent local means the run
65
+ could not see it, that a probe reporting zero calls was blind rather than
66
+ idle, and how to walk a trace from a symptom down to the value's origin.
67
+
68
+ ### Cost
69
+
70
+ - Redaction matches one union pattern instead of nine separate ones, checked
71
+ against 26 boundary names and 2,000 random names. Every capture path
72
+ shares the same redactor: failure snapshots, `probe`, `flt`, and `exec`.
73
+ - A failure snapshot still costs 44.126 microseconds for each raise-and-rescue
74
+ cycle, and a green test that raises nothing still costs nothing measurable.
75
+ `frames`, `preflight`, `flt`, and `exec` start a new process only when an
76
+ agent runs them; `probe` runs only around a block it selects.
77
+
78
+ ### Documentation
79
+
80
+ - `README.ja.md` translates the README. `README.md` stays the source of the
81
+ claims.
82
+ - `docs/design-decisions.md` records the observation limits found while
83
+ building this: `Coverage` cannot see lines under a `TracePoint` callback,
84
+ and neither can a probe, which is why bulldogger cannot probe its own
85
+ serialization path.
86
+
3
87
  ## 0.2.0
4
88
 
5
89
  ### A failing test now leads to the value that caused it
data/README.ja.md CHANGED
@@ -6,8 +6,7 @@ bulldogger は Ruby のテスト失敗を、コーディングエージェント
6
6
  各 JSON ファイルには、例外、バックトレース、取得したフレームの値が含まれます。
7
7
  失敗出力には、そのファイルの絶対パスが示されます。
8
8
 
9
- 開発と計測には Ruby 4.0.6 と debug 1.11.1 を使用しました。
10
- bulldogger 0.2.0 では独立したインスタンスを追加し、ドッグフーディングをユニットテストスイートまで拡張しました。
9
+ 開発とスナップショットの計測には Ruby 4.0.6 と debug 1.11.1 を使用しました。
11
10
 
12
11
  ## インストール
13
12
 
@@ -42,6 +41,10 @@ bulldogger skill path
42
41
  CLI には次のサブコマンドがあります。
43
42
 
44
43
  ```text
44
+ bulldogger frames -- command...
45
+ bulldogger preflight -- command...
46
+ bulldogger flt 'path:method#k' [--index path] -- command...
47
+ bulldogger exec 'path:method#k' --line N [--visit K] --statement text [--index path] -- command...
45
48
  bulldogger skill path
46
49
  bulldogger version
47
50
  bulldogger --version
@@ -77,7 +80,7 @@ observer.start
77
80
  ```
78
81
 
79
82
  各インスタンスは、設定、取得の購読、実行、証拠の状態を所有します。
80
- モジュールのファサードが提供する失敗、probe、record、SQLite 変換の各メソッドも利用できます。
83
+ モジュールのファサードが提供する失敗と probe の各メソッドも利用できます。
81
84
 
82
85
  インテグレーションには、デフォルトの代わりに指定したインスタンスを使えます。
83
86
 
@@ -89,24 +92,26 @@ Bulldogger::RSpec.instance = observer
89
92
  テストスイートが始まる前にインスタンスを指定します。
90
93
  この分離により、テストのセットアップがデフォルトインスタンスを置き換えても、外側のオブザーバーは動作を続けられます。
91
94
 
92
- ## 3 つの方法
95
+ ## コストモデル
93
96
 
94
- bulldogger は、実行時の証拠を収集する 3 つの方法を提供します。
97
+ 常時有効な処理は、失敗スナップショットを取得します。
98
+ 例外が発生しない green テストでは処理を行いません。
99
+ 監視した例外 1 件には microseconds 単位のコストがかかります。
95
100
 
96
- - 失敗スナップショットがデフォルトです。例外が発生しない green テストではデータを取得しません。伝播した例外については、フレームとローカル変数から答えを得られます。アサーションではアプリケーションの呼び出しがすでに戻っているため、bulldogger は完全な記録のもとでテストを 1 回リプレイします。
97
- - `probe` は、明示的な 1 回の実行中に指定したメソッドを監視します。
98
- - `record` は、明示的な 1 回の実行中にすべての Ruby メソッド呼び出しをトレースします。
101
+ スナップショットのベンチマークには Ruby 4.0.6 を使いました。
102
+ raise rescue 1 サイクルあたり 44.126 microseconds を計測しました。
103
+ そのうちフレーム取得には 1.288 microseconds がかかりました。
99
104
 
100
- 失敗したテストが証拠のパスをすでに示している場合は、失敗スナップショットを使います。
101
- 1 つのメソッドを調べる場合や、変更の前後で動作を比較する場合は `probe` を使います。
102
- 呼び出しシーケンス全体を追う必要がある場合は `record` を使います。
105
+ 再実行は明示的です。
106
+ `frames`、`preflight`、`flt`、`exec` は、ユーザーが実行したときだけ新しいプロセスを開始します。
107
+ `probe` も、ユーザーが選んだブロックの周囲でだけ動作します。
103
108
 
104
- ## 失敗出力
109
+ ## 失敗スナップショットから開始
105
110
 
106
111
  次のコマンドから、この出力を得ました。
107
112
 
108
113
  ```sh
109
- bundle exec ruby -Ilib test/fixtures/minitest_red/red_test.rb
114
+ bundle exec ruby -Ilib test/fixtures/minitest_red/red_test.rb --seed 12345
110
115
  ```
111
116
 
112
117
  ```text
@@ -115,16 +120,14 @@ RedTest#test_deep_raise:
115
120
  ArgumentError: expected 3 to equal the sum of [1, 2, 3]
116
121
  test/fixtures/minitest_red/app.rb:9:in 'Order.total'
117
122
  test/fixtures/minitest_red/red_test.rb:20:in 'RedTest#test_deep_raise'
118
- bulldogger evidence: /home/you/project/tmp/bulldogger/run-20260829-100406-58231/001-RedTest-test_deep_raise.json (raising method is in these frames)
123
+ bulldogger evidence: /home/you/project/tmp/bulldogger/run-20260831-192835-69510/001-RedTest-test_deep_raise.json (raising method is in these frames)
124
+ bulldogger rerun: bundle exec ruby -Itest test/fixtures/minitest_red/red_test.rb -n /\\Atest_deep_raise\\z/ --seed 12345
119
125
  ```
120
126
 
121
127
  括弧内の案内がある行のパスを開きます。
122
128
  このファイルには、1 件の失敗と取得した実行時の値が含まれます。
123
- この伝播した例外の証拠ファイルには `replay_skipped_reason: "application_frame_available"` があり、`Order.total` フレームにアプリケーションのローカル変数が含まれるため、リプレイは実行されませんでした。
124
-
125
- 失敗に対してリプレイを実行すると、`bulldogger replay:` 行が表示されます。
126
- 括弧内の説明は、再現した失敗または成功したリプレイを示します。
127
- 規則、設定、副作用については、[失敗したテストのリプレイ](#失敗したテストのリプレイ)を参照してください。
129
+ rerun 行には、そのテストと seed に対する完全なコマンドが含まれます。
130
+ bulldogger は、このコマンドを自動では実行しません。
128
131
 
129
132
  各実行では次の配置を使います。
130
133
 
@@ -134,67 +137,106 @@ tmp/bulldogger/
134
137
  run-20260829-100406-58231/
135
138
  001-RedTest-test_deep_raise.json
136
139
  002-RedTest-test_assertion_failure.json
137
- trace-001.jsonl
138
140
  index.json
139
141
  ```
140
142
 
141
143
  [`bulldogger` skill](skills/bulldogger/SKILL.md) は、エージェントがこれらのファイルを調べる方法を説明します。
142
144
  [証拠スキーマ](docs/evidence-schema.md)は、すべてのフィールドと取得モードを定義します。
143
145
 
144
- ## 失敗したテストのリプレイ
146
+ ## frames による分離実行のインデックス作成
147
+
148
+ rerun コマンドを `frames` の下で実行します。
149
+
150
+ ```sh
151
+ bulldogger frames -- bundle exec ruby -Itest test/fixtures/frames/minitest_frames_test.rb --seed 12345
152
+ ```
153
+
154
+ コマンドはインデックスのパスと子プロセスの結果を表示します。
145
155
 
146
- アサーションが例外を発生させる時点では、テスト対象のコードはすでに戻っています。
147
- この場合、失敗スナップショットにはテストフレームワークとテスト本体が含まれ、アプリケーションのフレームは含まれません。
148
- フレーム数を増やしても役に立たず、誤った値を生成した呼び出しはすでにスタックから外れています。
156
+ ```text
157
+ bulldogger frames: /home/you/project/tmp/bulldogger/frames-69833.jsonl
158
+ bulldogger result: pass (exit 0)
159
+ ```
149
160
 
150
- bulldogger は完全な記録のもとで失敗したテスト 1 件を再実行し、その値に到達します。
151
- 例外の形は異なり、例外の伝播中はアプリケーションコードがスタックに残ります。
152
- スナップショットにコードとローカル変数がすでに含まれるため、bulldogger はリプレイを省略します。
161
+ インデックスは各呼び出しにフレーム識別子 `fid` を付けます。
162
+ 形式は `path:method#k` です。
163
+ 数値は、テスト区間内にある同じメソッドの呼び出しを数えます。
164
+ インデックスには、アプリケーション、フレームワーク、gem のフレームが含まれます。
153
165
 
154
- デフォルトの規則は、テストファイル外にあるアプリケーションフレームを数えます。
155
- そのフレームが 0 件ならリプレイし、1 件以上なら `replay_skipped_reason: "application_frame_available"` を付けて省略します。
156
- この理由があり `replay` キーがない場合、フレームから答えを得られます。
166
+ ## preflight による分離再実行の検証
157
167
 
158
- リプレイは子プロセスで動くため、親テストスイートの結果は変わりません。
159
- green の実行ではリプレイを行わないため、green のときにコストがないという特性も保たれます。
160
- 追加コストはアサーション型の失敗後に限って発生し、デフォルトでは分離されたプロセスで 1 回だけ実行します。
168
+ `flt` または `exec` の前に `preflight` を実行します。
161
169
 
162
- 証拠には、トレースの絶対パスを持つ `replay` キーが追加されます。
163
- さらに `replay_reproduced` キーも追加されます。
164
- 子プロセスが失敗終了するとこのキーは `true` になり、成功すると `false` になります。
165
- `false` は、その失敗が単独では再現しなかったことを示します。
166
- 通常は、実行順序や別のテストとの共有状態に依存するテストを示唆します。
170
+ ```sh
171
+ bulldogger preflight -- bundle exec ruby -Itest test/fixtures/frames/minitest_frames_test.rb --seed 12345
172
+ ```
167
173
 
168
- 失敗出力は、有用な実行時データを含むファイルを示します。
174
+ 同じコマンドを別々のプロセスで 2 回実行します。
175
+ 2 つのアプリケーションフレーム列を比較します。
169
176
 
170
177
  ```text
171
- bulldogger evidence: /abs/path/evidence.json
172
- bulldogger replay: /abs/path/trace.jsonl (value was produced before the assertion raised)
178
+ bulldogger preflight: deterministic (app frames: 3)
179
+ bulldogger preflight indexes: /home/you/project/tmp/bulldogger/frames-70427.jsonl /home/you/project/tmp/bulldogger/frames-70432.jsonl
173
180
  ```
174
181
 
175
- 成功したリプレイでは `(test passed alone; this trace shows the passing run)` を使います。
176
- リプレイを実行できない場合、証拠の行は値の生成元がフレームにないことを示します。
177
- リプレイが無効な場合、証拠の括弧内には `BULLDOGGER_REPLAY=1` も示されます。
178
- 取得に失敗した場合、スナップショットにフレームがないことを示します。
182
+ preflight `deterministic` を表示した場合に限って、`flt` または `exec` を使います。
183
+ どちらの動詞もアプリケーションのフレーム識別子だけを受け付けます。
179
184
 
180
- リプレイの設定は次のとおりです。
185
+ ## flt による 1 フレームのトレース
181
186
 
182
- | 属性 | デフォルト | 効果 |
183
- |---|---|---|
184
- | `replay_on_failure` | `true` | テストファイル外のアプリケーションコードをフレームが含まない場合にリプレイします。`:always` はすべての失敗をリプレイします。`false` はリプレイしません。 |
185
- | `max_replays` | `1` | 1 回の実行に対するリプレイ数を制限します。 |
186
- | `replay_timeout` | `60` | bulldogger がリプレイの子プロセスを中止するまでの秒数です。中止したリプレイは `replay` キーも `replay_reproduced` キーも書きません。 |
187
+ アプリケーションの `fid` と同じ分離コマンドを渡します。
188
+
189
+ ```sh
190
+ bulldogger flt 'test/fixtures/flt/minitest_flt_test.rb:branchy#1' -- bundle exec ruby -Itest test/fixtures/flt/minitest_flt_test.rb --seed 12345
191
+ ```
192
+
193
+ ```text
194
+ bulldogger flt: /home/you/project/tmp/bulldogger/flt-77206.jsonl
195
+ bulldogger result: pass (exit 0)
196
+ ```
197
+
198
+ トレースには、フレーム開始、行ごとの変更、raise、return が含まれます。
199
+ 新しいローカル変数は `new` に、更新は `changed` に記録します。
200
+ スコープを外れたローカル変数は `out_of_scope` に列挙します。
201
+ ループ中間の反復は `skipped_iterations` レコードにまとめます。
202
+
203
+ `--index path` は、インデックスと再実行に同じコード状態マーカーを要求します。
204
+
205
+ ## exec による 1 文の評価
206
+
207
+ アプリケーションフレーム内の 1 回の行訪問を指定します。
208
+
209
+ ```sh
210
+ bulldogger exec 'test/fixtures/exec/minitest_exec_test.rb:threshold#1' --line 9 --statement 'binding.local_variable_set(:result, 10)' -- bundle exec ruby -Itest test/fixtures/exec/minitest_exec_test.rb --seed 12345 -n test_injection_can_change_the_outcome
211
+ ```
212
+
213
+ ```text
214
+ bulldogger exec: /home/you/project/tmp/bulldogger/exec-77267.jsonl
215
+ bulldogger value: 10
216
+ bulldogger result: pass (exit 0)
217
+ ```
218
+
219
+ `exec` は選択したフレームの binding で文を評価します。
220
+ デフォルトでは、その行の最初の訪問を選びます。
221
+ 後の訪問には `--visit K` を使います。
222
+ launcher は必要な `BULLDOGGER_EXEC=1` token を子プロセスへ渡します。
223
+ `--index path` は、同じコード状態マーカーを要求します。
224
+
225
+ 文はテストの動作を変え、副作用を発生させる可能性があります。
226
+ 変更後の結果を証拠に使う前に、結果ファイルを読みます。
187
227
 
188
- デフォルトの規則は、リプレイするテストを絞ります。
189
- リプレイしたテストは、ファイル書き込み、外部リクエスト、サンドボックスアカウントの変更などの各副作用を再度実行します。
190
- 1 つのプロセスでリプレイを無効にするには `BULLDOGGER_REPLAY=0` を設定します。
191
- すべての失敗をリプレイするには `BULLDOGGER_REPLAY=always` を設定します。
192
- アプリケーションで同じ方針を設定するには、`config.replay_on_failure` に `false` または `:always` を指定します。
193
- `BULLDOGGER_MAX_REPLAYS` は上限を上書きします。
194
- `BULLDOGGER_DISABLE=1` は、ほかのすべての取得とともにリプレイも無効にします。
228
+ ## RSpec の乱数 seed
195
229
 
196
- [リプレイのリファレンス](skills/bulldogger/references/replay.md)は、エージェントがトレースを値の生成元まで絞り込む方法を説明します。
197
- [トレーススキーマ](docs/trace-schema.md)は、リプレイトレースが持つイベントフィールドを定義します。
230
+ RSpec は seed を例の順序に使います。
231
+ RSpec は `Kernel.srand` を呼びません。
232
+ 例が `rand` を呼ぶ場合は、`RSpec.configure` 内に次の行を追加します。
233
+
234
+ ```ruby
235
+ config.before(:suite) { Kernel.srand config.seed }
236
+ ```
237
+
238
+ この行により、表示された rerun seed で乱数値を再現できます。
239
+ bulldogger はアプリケーションに代わって `Kernel.srand` を呼びません。
198
240
 
199
241
  ## probe によるメソッドの指定
200
242
 
@@ -242,33 +284,6 @@ result.fetch("identical")
242
284
  }
243
285
  ```
244
286
 
245
- ## 呼び出しシーケンスの記録
246
-
247
- 完全な呼び出しシーケンスが必要な場合は、対象を絞った 1 つの処理を囲みます。
248
-
249
- ```ruby
250
- trace_path = Bulldogger.record do
251
- run_related_test
252
- end
253
- ```
254
-
255
- 結果は JSONL ファイルであり、ヘッダーと call、return、raise の各イベントに対応するオブジェクトを含みます。
256
- 次の抜粋は、生成したトレースから得ました。
257
-
258
- ```jsonl
259
- {"schema_version":1,"kind":"record","events":["call","return","raise"],"limits":{"max_value_length":200}}
260
- {"event":"call","seq":1,"depth":1,"path":"-e","line":1,"method":"ProseTrace#outer","args":{"value":{"value":"3"}}}
261
- {"event":"return","seq":4,"depth":1,"path":"-e","line":1,"method":"ProseTrace#outer","return":{"value":"6"}}
262
- {"event":"raise","seq":7,"depth":2,"path":"-e","line":1,"method":"ProseTrace#inner","exception":{"class":"ArgumentError","message":"negative"}}
263
- {"event":"return","seq":8,"depth":2,"path":"-e","line":1,"method":"ProseTrace#inner","raised":true}
264
- ```
265
-
266
- [トレーススキーマ](docs/trace-schema.md)は、イベントフィールドと検証済みの `jq` クエリーを定義します。
267
-
268
- JSONL が主要な記録形式です。
269
- `sqlite3` gem が利用できる場合、`Bulldogger.trace_to_sqlite(trace_path, db_path)` は既存のトレースを変換します。
270
- 変換処理は soft require を使うため、bulldogger の実行時依存関係は 0 件のままです。
271
-
272
287
  ## コスト
273
288
 
274
289
  `TracePoint(:raise)` は、アプリケーションコードが rescue する例外を含む、発生したすべての例外を監視します。
@@ -298,22 +313,16 @@ green のテストスイートでも例外を発生させて rescue する場合
298
313
 
299
314
  ### 明示的な動詞のコスト
300
315
 
301
- 比例関係の計測ハーネスでは、両方の動詞に 1 つのアプリケーションフィクスチャを使いました。
302
316
  `probe` では対象メソッドの呼び出し 1 回あたり 1461.5 ns を計測しました。
303
- `record` ではトレース対象の呼び出し 1 回あたり 4249.5 ns を計測しました。
304
-
305
- `probe` のコストは対象メソッドの呼び出し数に比例し、計測ハーネスではその数を M と呼びます。
306
- `record` のコストはトレース対象の全呼び出し数に比例し、計測ハーネスではその数を N と呼びます。
307
- M/N が 0.25 のとき、このフィクスチャでは `probe` が 8.70x、`record` が 104.93x でした。
308
- これらの比率はこのアプリケーションフィクスチャに対する値であり、別のアプリケーションでは M/N の値が異なります。
317
+ フレームの gate では、対象外の呼び出し 1 件あたり約 105 ns を計測しました。
309
318
 
310
- record 専用の計測ハーネスでは、値の取得が 36.19x でした。
311
- JSONL への書き込みを含む完全な処理は 54.72x でした。
312
- 同じ機械で繰り返し計測すると、値の取得は 34x から 37x、完全な処理は 48x から 55x の範囲に収まりました。
313
- これらの数値は定数ではなく範囲として読んでください。
319
+ `flt` line event には約 0.5 microseconds の固定コストがあります。
320
+ 各ローカル変数の読み取りには、line event 1 件あたり約 0.1 microseconds が加わります。
321
+ 20 個のローカル変数と 10,000 件の line event を持つフレームには約 27 ms かかりました。
314
322
 
315
- `probe` `record` は、変更の前後に対象を絞って 1 回実行する明示的な動詞です。
316
- テストスイート全体へ継続的に適用しないでください。
323
+ これらの計測には Ruby 4.0.6 rubygems.org のテストスイートを使いました。
324
+ スイートには 4,925 件のテストがあり、seed 12345 と 1 worker を使いました。
325
+ 計測日は 2026-08-31 です。
317
326
 
318
327
  ## bulldogger の無効化
319
328
 
@@ -322,7 +331,6 @@ JSONL への書き込みを含む完全な処理は 54.72x でした。
322
331
 
323
332
  どちらかのスイッチを使うと、起動処理は `TracePoint(:raise)` の購読前に戻ります。
324
333
  証拠を書かず、実行ディレクトリを作らず、失敗に証拠の行を追加しません。
325
- リプレイは、このスイッチが省略する証拠処理から始まるため、実行されません。
326
334
  テストの終了コードと失敗数は変わりません。
327
335
  受け入れテストは、Minitest と RSpec についてこの動作を確認しています。
328
336
 
@@ -338,8 +346,6 @@ rescue を多用する green のテストスイートでは、bulldogger を無
338
346
  | `BULLDOGGER_DISABLED` | `1` | `BULLDOGGER_DISABLE` の別名です。 |
339
347
  | `BULLDOGGER_OUTPUT_DIR` | 空でないパス | デフォルトは作業ディレクトリからの相対パス `tmp/bulldogger` です。 |
340
348
  | `BULLDOGGER_FRAME_SOURCE` | `capture_frames` または `degraded` | デフォルトでは自動選択します。 |
341
- | `BULLDOGGER_REPLAY` | `0`、`1`、または `always` | デフォルトは `1` です。フレームから答えを得られない場合にリプレイします。`0` はリプレイを無効にします。`always` はすべての失敗をリプレイします。 |
342
- | `BULLDOGGER_MAX_REPLAYS` | 整数 | 1 回の実行に対するリプレイ数 `max_replays` を上書きします。デフォルトは `1` です。 |
343
349
 
344
350
  ## シークレットと上限
345
351
 
@@ -371,11 +377,11 @@ bulldogger は Hash をレンダリングするときにキーも検査します
371
377
 
372
378
  ## バージョン 0.2 の範囲
373
379
 
374
- Version 0.2 は、失敗スナップショット、失敗時の自動リプレイ、対象を指定した probe、明示的な完全記録、独立したインスタンスを提供します。
375
- JSON の証拠と JSONL のトレースを書き出します。
376
- Version 0.2 の境界は、ファイル成果物とオフラインの SQLite 変換機能を公開します。
380
+ Version 0.2 は、失敗スナップショット、probe、フレームインデックス、決定性検査、フレーム生存期間トレース、文の評価、独立したインスタンスを提供します。
381
+ JSON の証拠と JSONL の成果物を書き出します。
382
+ 重い取得は、明示的な動詞からだけ開始します。
377
383
 
378
- [設計判断](docs/design-decisions.md)は、3 つの方法と計測コストを説明します。
384
+ [設計判断](docs/design-decisions.md)は、証拠モデルと計測コストを説明します。
379
385
 
380
386
  ## ライセンス
381
387