branchproof 0.9.0 → 0.11.0

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/README.md +230 -28
  4. data/doc/Branchproof/Configuration.md +28 -0
  5. data/doc/Branchproof/CoveragePolicy.md +27 -0
  6. data/doc/Branchproof/Evidence.md +3 -0
  7. data/doc/Branchproof/FocusedReport.md +1 -1
  8. data/doc/Branchproof/Project.md +4 -1
  9. data/doc/Branchproof/RSpecAdapter/ClassRunnerGuard.md +11 -0
  10. data/doc/Branchproof/RSpecAdapter/ContextLifecycle.md +14 -0
  11. data/doc/Branchproof/RSpecAdapter/DefaultDiscovery.md +11 -0
  12. data/doc/Branchproof/RSpecAdapter/ExampleLifecycle.md +11 -0
  13. data/doc/Branchproof/RSpecAdapter/RunnerGuard.md +11 -0
  14. data/doc/Branchproof/RSpecAdapter/UnsupportedRunner.md +12 -0
  15. data/doc/Branchproof/RSpecAdapter.md +47 -0
  16. data/doc/Branchproof/RailsSupport.md +35 -2
  17. data/doc/Branchproof/Report.md +11 -2
  18. data/doc/Branchproof/ReportSelection.md +42 -0
  19. data/doc/Branchproof/Runtime.md +3 -0
  20. data/doc/Branchproof/Worker.md +8 -1
  21. data/doc/Branchproof.md +11 -1
  22. data/doc/CHANGELOG.md +42 -0
  23. data/doc/README.md +230 -28
  24. data/lib/branchproof/cli.rb +191 -38
  25. data/lib/branchproof/comparison.rb +58 -7
  26. data/lib/branchproof/configuration.rb +111 -0
  27. data/lib/branchproof/coverage_policy.rb +169 -0
  28. data/lib/branchproof/evidence.rb +41 -2
  29. data/lib/branchproof/focused_report.rb +149 -27
  30. data/lib/branchproof/project.rb +49 -2
  31. data/lib/branchproof/rails_support.rb +101 -0
  32. data/lib/branchproof/report.rb +118 -10
  33. data/lib/branchproof/report_selection.rb +158 -0
  34. data/lib/branchproof/rspec_adapter.rb +423 -0
  35. data/lib/branchproof/runtime.rb +4 -0
  36. data/lib/branchproof/saved_report.rb +38 -5
  37. data/lib/branchproof/version.rb +1 -1
  38. data/lib/branchproof/worker.rb +75 -15
  39. data/lib/branchproof.rb +4 -0
  40. data/llms.txt +11 -1
  41. data/sig/branchproof.rbs +63 -4
  42. metadata +16 -2
@@ -6,16 +6,31 @@
6
6
 
7
7
  Boots the selected Rails application after Branchproof's load hook is active.
8
8
 
9
+ ## Constants
10
+ ### `SUPPORTED_CAPYBARA_DRIVER` <a id="constant-SUPPORTED_CAPYBARA_DRIVER"></a> <a id="SUPPORTED_CAPYBARA_DRIVER-constant"></a>
11
+ Not documented.
12
+
13
+ ### `SUPPORTED_RAILS` <a id="constant-SUPPORTED_RAILS"></a> <a id="SUPPORTED_RAILS-constant"></a>
14
+ Not documented.
15
+
16
+ ### `SUPPORTED_RSPEC_RAILS_MAJOR` <a id="constant-SUPPORTED_RSPEC_RAILS_MAJOR"></a> <a id="SUPPORTED_RSPEC_RAILS_MAJOR-constant"></a>
17
+ Not documented.
18
+
9
19
  ## Public Class Methods
10
20
  ### `boot(project:)` <a id="method-c-boot"></a> <a id="boot-class_method"></a>
11
- Not documented.
21
+ Boot the Rails application for the native Minitest adapter.
12
22
 
13
23
  ### `boot_environment(environment)` <a id="method-c-boot_environment"></a> <a id="boot_environment-class_method"></a>
14
- Not documented.
24
+ Require the application and validate the process-wide Rails policy. This
25
+ method is public so adapter-specific helpers can own the remaining test
26
+ framework setup while sharing the exact same Rails boot checks.
15
27
 
16
28
  ### `environment_path(project)` <a id="method-c-environment_path"></a> <a id="environment_path-class_method"></a>
17
29
  Not documented.
18
30
 
31
+ ### `install_rspec_driver_guard!()` <a id="method-c-install_rspec_driver_guard-21"></a> <a id="install_rspec_driver_guard!-class_method"></a>
32
+ Not documented.
33
+
19
34
  ### `metadata()` <a id="method-c-metadata"></a> <a id="metadata-class_method"></a>
20
35
  Not documented.
21
36
 
@@ -25,8 +40,26 @@ Not documented.
25
40
  ### `reloading_enabled?(config)` <a id="method-c-reloading_enabled-3F"></a> <a id="reloading_enabled?-class_method"></a>
26
41
  - **@return** [Boolean]
27
42
 
43
+ ### `rspec_rails_version()` <a id="method-c-rspec_rails_version"></a> <a id="rspec_rails_version-class_method"></a>
44
+ Not documented.
45
+
28
46
  ### `validate_application!()` <a id="method-c-validate_application-21"></a> <a id="validate_application!-class_method"></a>
29
47
  - **@raise** [Error]
30
48
 
49
+ ### `validate_rspec!(project:)` <a id="method-c-validate_rspec-21"></a> <a id="validate_rspec!-class_method"></a>
50
+ Validate the seam after an RSpec Rails helper has loaded. RSpec owns requiring
51
+ rspec/rails and configuring its example groups; this method only checks that
52
+ that setup is attached to the same, already-validated Rails application.
53
+ - **@raise** [Error]
54
+
55
+ ### `validate_rspec_driver!(example_metadata)` <a id="method-c-validate_rspec_driver-21"></a> <a id="validate_rspec_driver!-class_method"></a>
56
+ RSpec Rails invokes this at example execution time, before the example can ask
57
+ Capybara to launch a browser. The adapter converts this explicit rejection
58
+ into an incomplete/error execution result.
59
+ - **@raise** [Error]
60
+
31
61
  ### `validate_test_environment()` <a id="method-c-validate_test_environment"></a> <a id="validate_test_environment-class_method"></a>
32
62
  - **@raise** [Error]
63
+
64
+ ### `validate_version_tuple!(rails_metadata)` <a id="method-c-validate_version_tuple-21"></a> <a id="validate_version_tuple!-class_method"></a>
65
+ - **@raise** [Error]
@@ -24,7 +24,7 @@ Not documented.
24
24
  Not documented.
25
25
 
26
26
  ## Public Class Methods
27
- ### `from_document(document:, level: = nil, view: = :decisions, missing_only: = false)` <a id="method-c-from_document"></a> <a id="from_document-class_method"></a>
27
+ ### `from_document(document:, level: = nil, view: = :decisions, missing_only: = false, focus: = nil, top: = nil, minimum: = nil)` <a id="method-c-from_document"></a> <a id="from_document-class_method"></a>
28
28
  Not documented.
29
29
 
30
30
  ## Public Instance Methods
@@ -38,6 +38,12 @@ Shares the existing missing-case wording with focused terminal views.
38
38
  ### `coverage_ladder_lines()` <a id="method-i-coverage_ladder_lines"></a> <a id="coverage_ladder_lines-instance_method"></a>
39
39
  Render the shared ladder in every terminal view.
40
40
 
41
+ ### `coverage_policy()` <a id="method-i-coverage_policy"></a> <a id="coverage_policy-instance_method"></a>
42
+ Not documented.
43
+
44
+ ### `coverage_policy_lines()` <a id="method-i-coverage_policy_lines"></a> <a id="coverage_policy_lines-instance_method"></a>
45
+ Not documented.
46
+
41
47
  ### `coverage_status_label(status)` <a id="method-i-coverage_status_label"></a> <a id="coverage_status_label-instance_method"></a>
42
48
  Not documented.
43
49
 
@@ -56,9 +62,12 @@ Formats source context consistently in live and saved terminal views.
56
62
  ### `exit_code()` <a id="method-i-exit_code"></a> <a id="exit_code-instance_method"></a>
57
63
  Not documented.
58
64
 
59
- ### `initialize(inventory:, evidence:, analysis:, minima:, baseline:, diagnostics:, level: = 3, missing_only: = false, view: = :decisions, run_metadata: = {}, saved_document: = nil)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
65
+ ### `initialize(inventory:, evidence:, analysis:, minima:, baseline:, diagnostics:, level: = 3, missing_only: = false, view: = :decisions, run_metadata: = {}, saved_document: = nil, focus: = nil, top: = nil, minimum: = nil)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
60
66
  - **@raise** [ArgumentError]
61
67
  - **@return** [Report] a new instance of Report
62
68
 
69
+ ### `rerun_command(test_id)` <a id="method-i-rerun_command"></a> <a id="rerun_command-instance_method"></a>
70
+ Not documented.
71
+
63
72
  ### `write(io:, format:)` <a id="method-i-write"></a> <a id="write-instance_method"></a>
64
73
  - **@raise** [ArgumentError]
@@ -0,0 +1,42 @@
1
+ # Class Branchproof::ReportSelection <a id="class-Branchproof-ReportSelection"></a>
2
+
3
+ | | |
4
+ | --- | --- |
5
+ | **Inherits** | Object |
6
+ | **Defined in** | lib/branchproof/report_selection.rb |
7
+
8
+ Validates and applies terminal-only report display filters. rubocop:disable
9
+ Metrics/ClassLength, Metrics/AbcSize, Metrics/CyclomaticComplexity,
10
+ Metrics/MethodLength, Metrics/PerceivedComplexity
11
+
12
+ ## Attributes
13
+ ### `focus` [R] <a id="attribute-i-focus"></a> <a id="focus-instance_method"></a>
14
+ Returns the value of attribute focus.
15
+
16
+ ### `top` [R] <a id="attribute-i-top"></a> <a id="top-instance_method"></a>
17
+ Returns the value of attribute top.
18
+
19
+ ## Public Instance Methods
20
+ ### `active?()` <a id="method-i-active-3F"></a> <a id="active?-instance_method"></a>
21
+ - **@return** [Boolean]
22
+
23
+ ### `filter_decisions(decisions, inventory: = {})` <a id="method-i-filter_decisions"></a> <a id="filter_decisions-instance_method"></a>
24
+ Not documented.
25
+
26
+ ### `focus_active?()` <a id="method-i-focus_active-3F"></a> <a id="focus_active?-instance_method"></a>
27
+ - **@return** [Boolean]
28
+
29
+ ### `focus_label()` <a id="method-i-focus_label"></a> <a id="focus_label-instance_method"></a>
30
+ Not documented.
31
+
32
+ ### `initialize(focus: = nil, top: = nil)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
33
+ - **@return** [ReportSelection] a new instance of ReportSelection
34
+
35
+ ### `limit(items)` <a id="method-i-limit"></a> <a id="limit-instance_method"></a>
36
+ Not documented.
37
+
38
+ ### `matching_decision_ids(document)` <a id="method-i-matching_decision_ids"></a> <a id="matching_decision_ids-instance_method"></a>
39
+ Not documented.
40
+
41
+ ### `sort_key(decision, inventory: = {})` <a id="method-i-sort_key"></a> <a id="sort_key-instance_method"></a>
42
+ Not documented.
@@ -108,6 +108,9 @@ Metrics/PerceivedComplexity
108
108
  ### `snapshot()` <a id="method-c-snapshot"></a> <a id="snapshot-class_method"></a>
109
109
  Not documented.
110
110
 
111
+ ### `test_phase_counts()` <a id="method-c-test_phase_counts"></a> <a id="test_phase_counts-class_method"></a>
112
+ Not documented.
113
+
111
114
  ### `value_path(decision_id, value, domain)` <a id="method-c-value_path"></a> <a id="value_path-class_method"></a>
112
115
  rubocop:disable-next Metrics/MethodLength -- trace state branches are
113
116
  explicit. rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity,
@@ -4,9 +4,12 @@
4
4
  | --- | --- |
5
5
  | **Defined in** | lib/branchproof/worker.rb |
6
6
 
7
- Runs one isolated Minitest worker and atomically exports its result.
7
+ Runs one isolated test worker and atomically exports its result.
8
8
 
9
9
  ## Public Class Methods
10
+ ### `adapter_for(project, runtime)` <a id="method-c-adapter_for"></a> <a id="adapter_for-class_method"></a>
11
+ Not documented.
12
+
10
13
  ### `boot_project(project)` <a id="method-c-boot_project"></a> <a id="boot_project-class_method"></a>
11
14
  Not documented.
12
15
 
@@ -17,6 +20,10 @@ Not documented.
17
20
  This result boundary intentionally carries the complete worker payload.
18
21
  rubocop:disable-next Metrics/ParameterLists
19
22
 
23
+ ### `guard_late_rspec_execution(adapter, payload)` <a id="method-c-guard_late_rspec_execution"></a> <a id="guard_late_rspec_execution-class_method"></a>
24
+ Installed before application hooks, so this runs after their at_exit work. A
25
+ rescued second runner must still invalidate the already-exported result.
26
+
20
27
  ### `incomplete_evidence(evidence, diagnostics)` <a id="method-c-incomplete_evidence"></a> <a id="incomplete_evidence-class_method"></a>
21
28
  Not documented.
22
29
 
data/doc/Branchproof.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  | | |
4
4
  | --- | --- |
5
- | **Defined in** | lib/branchproof.rb, lib/branchproof/cli.rb, lib/branchproof/limits.rb, lib/branchproof/loader.rb, lib/branchproof/report.rb, lib/branchproof/source.rb, lib/branchproof/worker.rb, lib/branchproof/project.rb, lib/branchproof/records.rb, lib/branchproof/runtime.rb, lib/branchproof/version.rb, lib/branchproof/analyzer.rb, lib/branchproof/evidence.rb, lib/branchproof/minimizer.rb, lib/branchproof/comparison.rb, lib/branchproof/constraints.rb, lib/branchproof/instrumenter.rb, lib/branchproof/runtime_flow.rb, lib/branchproof/saved_report.rb, lib/branchproof/value_syntax.rb, lib/branchproof/rails_support.rb, lib/branchproof/value_runtime.rb, lib/branchproof/coverage_index.rb, lib/branchproof/decision_table.rb, lib/branchproof/default_syntax.rb, lib/branchproof/focused_report.rb, lib/branchproof/decision_syntax.rb, lib/branchproof/default_runtime.rb, lib/branchproof/exception_syntax.rb, lib/branchproof/iteration_syntax.rb, lib/branchproof/minitest_adapter.rb, lib/branchproof/comparison_report.rb, lib/branchproof/exception_runtime.rb, lib/branchproof/iteration_runtime.rb, lib/branchproof/flow_instrumentation.rb, lib/branchproof/value_instrumentation.rb, lib/branchproof/default_instrumentation.rb, lib/branchproof/exception_instrumentation.rb, lib/branchproof/iteration_instrumentation.rb, lib/branchproof/extended_alternative_runtime.rb |
5
+ | **Defined in** | lib/branchproof.rb, lib/branchproof/cli.rb, lib/branchproof/limits.rb, lib/branchproof/loader.rb, lib/branchproof/report.rb, lib/branchproof/source.rb, lib/branchproof/worker.rb, lib/branchproof/project.rb, lib/branchproof/records.rb, lib/branchproof/runtime.rb, lib/branchproof/version.rb, lib/branchproof/analyzer.rb, lib/branchproof/evidence.rb, lib/branchproof/minimizer.rb, lib/branchproof/comparison.rb, lib/branchproof/constraints.rb, lib/branchproof/instrumenter.rb, lib/branchproof/runtime_flow.rb, lib/branchproof/saved_report.rb, lib/branchproof/value_syntax.rb, lib/branchproof/configuration.rb, lib/branchproof/rails_support.rb, lib/branchproof/rspec_adapter.rb, lib/branchproof/value_runtime.rb, lib/branchproof/coverage_index.rb, lib/branchproof/decision_table.rb, lib/branchproof/default_syntax.rb, lib/branchproof/focused_report.rb, lib/branchproof/coverage_policy.rb, lib/branchproof/decision_syntax.rb, lib/branchproof/default_runtime.rb, lib/branchproof/exception_syntax.rb, lib/branchproof/iteration_syntax.rb, lib/branchproof/minitest_adapter.rb, lib/branchproof/report_selection.rb, lib/branchproof/comparison_report.rb, lib/branchproof/exception_runtime.rb, lib/branchproof/iteration_runtime.rb, lib/branchproof/flow_instrumentation.rb, lib/branchproof/value_instrumentation.rb, lib/branchproof/default_instrumentation.rb, lib/branchproof/exception_instrumentation.rb, lib/branchproof/iteration_instrumentation.rb, lib/branchproof/extended_alternative_runtime.rb |
6
6
 
7
7
  Keep source-boundary and parameter-binding rules together for auditing.
8
8
  rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity,
@@ -18,9 +18,11 @@ Not documented.
18
18
  - [Branchproof/CLI.md](Branchproof/CLI.md)
19
19
  - [Branchproof/Comparison.md](Branchproof/Comparison.md)
20
20
  - [Branchproof/ComparisonReport.md](Branchproof/ComparisonReport.md)
21
+ - [Branchproof/Configuration.md](Branchproof/Configuration.md)
21
22
  - [Branchproof/Constraints/Solver.md](Branchproof/Constraints/Solver.md)
22
23
  - [Branchproof/Constraints.md](Branchproof/Constraints.md)
23
24
  - [Branchproof/CoverageIndex.md](Branchproof/CoverageIndex.md)
25
+ - [Branchproof/CoveragePolicy.md](Branchproof/CoveragePolicy.md)
24
26
  - [Branchproof/DecisionSyntax.md](Branchproof/DecisionSyntax.md)
25
27
  - [Branchproof/DecisionTable.md](Branchproof/DecisionTable.md)
26
28
  - [Branchproof/DefaultInstrumentation.md](Branchproof/DefaultInstrumentation.md)
@@ -43,10 +45,18 @@ Not documented.
43
45
  - [Branchproof/Minimizer.md](Branchproof/Minimizer.md)
44
46
  - [Branchproof/MinitestAdapter.md](Branchproof/MinitestAdapter.md)
45
47
  - [Branchproof/Project.md](Branchproof/Project.md)
48
+ - [Branchproof/RSpecAdapter/ClassRunnerGuard.md](Branchproof/RSpecAdapter/ClassRunnerGuard.md)
49
+ - [Branchproof/RSpecAdapter/ContextLifecycle.md](Branchproof/RSpecAdapter/ContextLifecycle.md)
50
+ - [Branchproof/RSpecAdapter/DefaultDiscovery.md](Branchproof/RSpecAdapter/DefaultDiscovery.md)
51
+ - [Branchproof/RSpecAdapter/ExampleLifecycle.md](Branchproof/RSpecAdapter/ExampleLifecycle.md)
52
+ - [Branchproof/RSpecAdapter/RunnerGuard.md](Branchproof/RSpecAdapter/RunnerGuard.md)
53
+ - [Branchproof/RSpecAdapter/UnsupportedRunner.md](Branchproof/RSpecAdapter/UnsupportedRunner.md)
54
+ - [Branchproof/RSpecAdapter.md](Branchproof/RSpecAdapter.md)
46
55
  - [Branchproof/RailsSupport/Error.md](Branchproof/RailsSupport/Error.md)
47
56
  - [Branchproof/RailsSupport.md](Branchproof/RailsSupport.md)
48
57
  - [Branchproof/Records.md](Branchproof/Records.md)
49
58
  - [Branchproof/Report.md](Branchproof/Report.md)
59
+ - [Branchproof/ReportSelection.md](Branchproof/ReportSelection.md)
50
60
  - [Branchproof/Runtime.md](Branchproof/Runtime.md)
51
61
  - [Branchproof/RuntimeFlow.md](Branchproof/RuntimeFlow.md)
52
62
  - [Branchproof/SavedReport.md](Branchproof/SavedReport.md)
data/doc/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.11.0] - 2026-09-25
4
+
5
+ - Add validated coverage policies for Decision, Condition, Condition/Decision,
6
+ MC/DC, and Decision Table criteria. Repeatable `--minimum criterion=threshold`
7
+ options override matching project or saved-report policy entries, compare
8
+ exact counts, and distinguish failed gates from unavailable or incomplete
9
+ evidence in exit status and JSON.
10
+ - Persist policy decisions in live schema `1.4` reports while keeping saved
11
+ schemas `1.0` through `1.3` readable; offline policy overrides do not load
12
+ project configuration or mutate snapshots.
13
+ - Add terminal-only `--focus PATH[:LINE]` and `--top N` selection for analyze
14
+ and report views. Selection uses captured source spans and deterministic source
15
+ order while leaving global summaries, gates, diagnostics, and exit status
16
+ unchanged.
17
+ - Document a GitHub Actions artifact workflow that creates the output directory
18
+ and uploads reports after gate failures.
19
+ - Preserve setup, body, and teardown phase totals when independent evidence runs
20
+ are merged, keeping phase attribution complete in combined snapshots.
21
+ - Add declarative `.branchproof.json` project configuration with CLI precedence,
22
+ source exclusions, and comparison metadata for repeatable coverage runs.
23
+ - Add a pipeline benchmark harness that exposes timing dimensions for future
24
+ measurements of coverage feedback costs.
25
+
26
+ ## [0.10.0] - 2026-09-21
27
+
28
+ - Add the first-release RSpec execution path for plain Ruby and Rails projects.
29
+ It supports RSpec 3.13, including Rails 8.1 with rspec-rails 8.x on CRuby 3.4.
30
+ Integration is verified against rspec-rails 8.0.4 and native RSpec behavior.
31
+ - Add `--framework auto|minitest|rspec`, native RSpec discovery that respects
32
+ exclusions and custom patterns, full-description labels, and status mapping for pending,
33
+ skipped, fixed pending, and failed examples.
34
+ - Keep Rails helper ownership with the application after Branchproof's loader;
35
+ transaction behavior, in-process specs, and context/suite/unattributed
36
+ evidence are recorded explicitly.
37
+ - Allow focused pure-Ruby specs inside Rails projects without requiring Rails boot.
38
+ - Match RSpec examples across saved reports only when spec and declaration
39
+ revisions agree, preventing false matches after examples are inserted or reordered.
40
+ - Stream test progress to stderr, show quoted RSpec rerun commands, and explain
41
+ unmatched test globs, empty suites, and filters selecting no examples.
42
+ - Document repeated ordinary and shared-example benchmarks with allocations
43
+ and peak process memory; end-to-end overhead remains substantial.
44
+
3
45
  ## [0.9.0] - 2026-09-18
4
46
 
5
47
  - Measure contextual predicates, guarded pattern selection, dynamic case splat
data/doc/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Branchproof
2
2
 
3
3
  Branchproof measures decision, condition, modified condition/decision (MC/DC),
4
- and decision-table coverage from one serial Minitest run. It discovers Ruby
4
+ and decision-table coverage from one serial Minitest or RSpec run. It discovers Ruby
5
5
  decisions through Prism, records their runtime paths, and attributes evidence
6
6
  to tests. Boolean decisions receive the coverage ladder; `case`, pattern
7
7
  alternatives, safe navigation, and conditional assignments receive alternative
@@ -22,10 +22,13 @@ Or add it to a bundle:
22
22
  bundle add branchproof
23
23
  ```
24
24
 
25
- Branchproof targets CRuby 3.3 and 3.4, Minitest 5.x, and Prism 1.x. The
26
- published core runtime matrix is the CI matrix. Rails support is optional and
27
- has been locally checked against Rails 8.1.3.1 on CRuby 3.4.5; Rails is
28
- supplied by the application and is not a runtime dependency of this gem.
25
+ Branchproof targets CRuby 3.3 and 3.4, Minitest 5.x, RSpec 3.13, and Prism 1.x. The
26
+ published core runtime matrix is the CI matrix. Rails/RSpec execution is limited
27
+ to Rails 8.1.x, rspec-rails 8.x, RSpec 3.13.x, and CRuby 3.4.x; passing core
28
+ tests on Ruby 4.0 does not imply Rails/RSpec support. Integration has been checked
29
+ with Rails 8.1.3.1, rspec-rails 8.0.4, RSpec Core 3.13.6, and CRuby 3.4.7.
30
+ Rails and RSpec are optional dependencies supplied by the application.
31
+ Minitest 5.x remains a runtime dependency of this gem.
29
32
  Unsupported syntax and incomplete observations remain visible in the report
30
33
  instead of being counted as coverage.
31
34
 
@@ -33,7 +36,7 @@ instead of being counted as coverage.
33
36
 
34
37
  Run `branchproof analyze` with the source files or globs to inspect, followed by
35
38
  options. A second `--` separates Branchproof options from arguments passed to
36
- Minitest unchanged:
39
+ the selected test framework:
37
40
 
38
41
  ```sh
39
42
  branchproof analyze 'lib/**/*.rb' --test 'test/**/*_test.rb' \
@@ -58,22 +61,126 @@ the application's bundle and Rails version remain in effect:
58
61
  bundle exec branchproof analyze 'app/**/*.rb' --project rails --test 'test/**/*_test.rb'
59
62
  ```
60
63
 
64
+ Choose the test framework independently from project kind:
65
+
66
+ ```sh
67
+ bundle exec branchproof analyze 'app/**/*.rb' --project rails --framework rspec
68
+ ```
69
+
70
+ `--framework auto|minitest|rspec` is explicit about the adapter. Auto mode
71
+ selects RSpec when `.rspec` or `spec/**/*_spec.rb` markers exist and Minitest
72
+ when `test/**/*_test.rb` or `test/**/test_*.rb` markers exist. If both are
73
+ present, specify the framework. RSpec discovery uses `spec/**/*_spec.rb`; an
74
+ explicit `--test` selection remains authoritative. Project roots are resolved
75
+ explicitly, and report paths are relative to that root so reports from
76
+ different checkouts can be compared.
77
+
78
+ Install `rspec` in the application's test bundle; Rails applications also need
79
+ `rspec-rails`. RSpec reads its usual option files and `SPEC_OPTS`, including
80
+ helper requires, filters, ordering, and file or example-ID selectors. Selectors
81
+ from RSpec configuration replace default discovery. Combining those selectors
82
+ with an explicit Branchproof `--test` is rejected as ambiguous; ordinary filters
83
+ such as `--tag` and `--example` can accompany `--test`.
84
+
85
+ RSpec before hooks, eager `let!`, and around-hook prefixes own setup evidence.
86
+ The example body and helpers evaluated there own body evidence; after hooks,
87
+ mock cleanup, and around-hook suffixes own teardown evidence. Suite and context
88
+ hooks remain unattributed. Pending and skipped examples do not invent execution;
89
+ an unexpectedly passing pending example remains a failure.
90
+
91
+ RSpec support is serial: dry-run, bisect, DRb, custom runners, nested runs, and
92
+ repeated example attempts are rejected. Rails/RSpec supports Rails 8.1.x with
93
+ rspec-rails 8.x on CRuby 3.4.x. Feature and system specs use the in-process
94
+ Capybara `rack_test` driver; browser drivers require execution-context support
95
+ outside this release. Capybara is optional for apps that do not use those specs.
96
+
61
97
  For a project rooted at the current directory, `--project auto` is the
62
98
  default. It selects Rails only when both `config/application.rb` and
63
99
  `config/environment.rb` exist; otherwise it selects a plain Ruby project.
64
100
  Use `--project ruby` or `--project rails` to override detection. An explicit
65
101
  Rails project without both boot files is a usage error.
66
102
 
67
- Project defaults discover the sorted, de-duplicated union of
103
+ Minitest defaults discover the sorted, de-duplicated union of
68
104
  `test/**/*_test.rb` and `test/**/test_*.rb`. Helper, support, and fixture files
69
105
  are excluded from that default set. `--test` remains authoritative when test
70
106
  files are selected explicitly. The worker prepends the project's `lib` and
71
- `test` directories to its child load path, so application `require` calls
107
+ `test` directories (`lib` and `spec` for RSpec) to its child load path, so application `require` calls
72
108
  resolve without changing the parent process.
73
109
 
74
- Rails analysis boots `config/environment.rb` and `rails/test_help` inside the
75
- isolated worker after Branchproof's loader and Minitest hooks are installed. The
76
- child receives `RAILS_ENV=test`, `RACK_ENV=test`, `PARALLEL_WORKERS=1`,
110
+ Project defaults can be checked into `.branchproof.json` at the project root:
111
+
112
+ ```json
113
+ {
114
+ "schema_version": 1,
115
+ "project": "rails",
116
+ "framework": "minitest",
117
+ "sources": ["app/**/*.rb"],
118
+ "tests": ["test/**/*_test.rb"],
119
+ "exclude": ["app/generated/**/*.rb"],
120
+ "minimum": { "mcdc": 80 }
121
+ }
122
+ ```
123
+
124
+ `project` accepts `auto`, `ruby`, or `rails`; `framework` accepts `auto`,
125
+ `minitest`, or `rspec`. `sources` and `tests` replace their corresponding
126
+ defaults, while `exclude` removes matching source files before inventory. A
127
+ configuration file may live elsewhere when passed with `--config PATH`; its
128
+ patterns are still resolved from the project root. Use `--no-config` to disable
129
+ the default file. Command-line project, framework, source, and test selections
130
+ take precedence over the file. Configured `exclude` patterns are applied
131
+ before source inventory and are recorded in run metadata for comparison.
132
+ `--config` and `--no-config` cannot be used together. RSpec configuration
133
+ selectors continue to conflict with an explicit test selection, including one
134
+ supplied by this file.
135
+
136
+ The `minimum` values are validated percentages from 0 through 100 for the five
137
+ supported criteria: `decision`, `condition`, `condition_decision`, `mcdc`, and
138
+ `decision_table`. A policy is evaluated against the exact numerator and
139
+ denominator counts in the report, so a value such as `66.67` is not rounded
140
+ before it is compared. The policy is recorded in JSON under
141
+ `coverage_policy`.
142
+
143
+ The command line can add or replace individual policy entries with repeatable
144
+ `--minimum` options. Use `criterion=threshold`, for example:
145
+
146
+ ```sh
147
+ bundle exec branchproof analyze 'lib/**/*.rb' --minimum mcdc=80 \
148
+ --minimum decision_table=75
149
+ ```
150
+
151
+ CLI values take precedence over matching `minimum` keys from `.branchproof.json`.
152
+ Specifying the same criterion more than once on the command line is an error.
153
+ The five criteria are evaluated independently. A threshold met exactly passes;
154
+ a result below it fails. A requested gate with a zero denominator, unavailable
155
+ coverage, or incomplete observations or analysis is `unavailable`, and exits
156
+ with status 2. A below-threshold gate exits 1. A failed test run makes
157
+ requested policy gates unavailable and exits 2; without a policy, a failed test
158
+ run retains the existing exit status 1. With no policy, the existing report
159
+ exit behavior remains unchanged.
160
+
161
+ For a Minitest project, a focused configuration can select the library and
162
+ test trees directly:
163
+
164
+ ```json
165
+ { "schema_version": 1, "framework": "minitest",
166
+ "sources": ["lib/**/*.rb"], "tests": ["test/**/*_test.rb"] }
167
+ ```
168
+
169
+ For an RSpec project, use its spec patterns instead:
170
+
171
+ ```json
172
+ { "schema_version": 1, "framework": "rspec",
173
+ "sources": ["app/**/*.rb"], "tests": ["spec/**/*_spec.rb"] }
174
+ ```
175
+
176
+ Rails analysis boots the application inside the isolated worker after
177
+ Branchproof's loader and the selected framework hooks are installed. The
178
+ application's `rails_helper` owns requiring and configuring `rspec/rails` after
179
+ the loader; the helper must not require `branchproof` again. Selected specs
180
+ may require only `spec_helper` and run without booting Rails,
181
+ even when project detection selects Rails. Once Rails is loaded, Branchproof
182
+ requires an initialized application and enforces the Rails/RSpec support limits.
183
+ The child receives `RAILS_ENV=test`, `RACK_ENV=test`, `PARALLEL_WORKERS=1`,
77
184
  `DISABLE_BOOTSNAP=1`, and `DISABLE_SPRING=1`; the invoking process environment
78
185
  is unchanged. The Rails metadata in the report identifies the selected
79
186
  project and Rails version.
@@ -93,6 +200,17 @@ the witness pair or missing counterpart constraints beside each condition;
93
200
  Level 2 and Level 3 also list named supporting tests. Repeated supporting-set
94
201
  rows are collapsed in terminal output only.
95
202
 
203
+ RSpec owners use the example's `full_description` and positional `example_id`.
204
+ Terminal views include quoted rerun commands, including selectors for shared
205
+ examples. These selectors apply to the recorded spec revision. Comparisons
206
+ require matching spec and declaration digests before matching example owners;
207
+ changed specs and older reports without digests are treated conservatively.
208
+ Runner output streams to stderr while JSON reports remain on stdout.
209
+ Pending, skipped, fixed-pending, and failed examples map to the corresponding
210
+ baseline statuses; suite and context lifecycle events remain visible, and
211
+ observations without a test owner are counted as unattributed. The same levels
212
+ and terminal views are available for both adapters.
213
+
96
214
  For example, running the contents of the small `decision.rb` /
97
215
  `test_decision_test.rb` fixture from a project `lib/` and `test/` directory
98
216
  with the terminal format produces a summary like this:
@@ -156,6 +274,15 @@ that did evaluate count toward Condition Coverage even when their value was
156
274
  masked by another condition. Here, the two observations prove independence
157
275
  for `logged_in?`; `admin?` still needs `[TF] => F`.
158
276
 
277
+ This is also a compact test-design walkthrough. A test that records
278
+ `[F-] => F` proves the false branch of `logged_in?`; a test that records
279
+ `[TT] => T` proves the true branch and establishes the current positive path.
280
+ Adding a test that records `[TF] => F` evaluates `admin?` false while
281
+ `logged_in?` stays true, so it supplies the missing independence witness and
282
+ improves the condition, Condition/Decision, MC/DC, and applicable decision-table
283
+ results. The report names the missing vector and its owning test when it is
284
+ observed.
285
+
159
286
  Decision and Condition Coverage are calculated independently from the captured
160
287
  evidence. Condition/Decision requires both; MC/DC adds independence evidence.
161
288
  Decision Table Coverage is calculated independently of all of them: MC/DC asks
@@ -449,6 +576,34 @@ execution. JSON output always contains the complete evidence document, so an
449
576
  explicit view cannot be combined with `--format json`. The `mcdc` executable
450
577
  accepts the same arguments for existing scripts.
451
578
 
579
+ Terminal reports can be narrowed with `--focus PATH[:LINE]` and bounded with
580
+ `--top N`, where `N` is a positive integer. These options are accepted by
581
+ `analyze` and `report`, and are terminal-only. Focus matches the source spans
582
+ captured in the report; rendering does not reopen or need the original source
583
+ file. `--focus` and `--top` affect displayed detail only: the coverage summary,
584
+ policy gates, diagnostics, exit status, and global counts remain unchanged.
585
+
586
+ The default view limits decisions. In `--view decision-tables`, the units are
587
+ decision tables; in `--view conditions`, condition and alternative rows; and
588
+ in `--view tests`, test rows. Rows are ordered deterministically by project-
589
+ relative path and source line, with deterministic tie-breakers for each view:
590
+ decisions use column and stable ID; decision tables use decision ID; conditions
591
+ and alternatives use column, decision ID, and condition or alternative index;
592
+ tests use test name and ID. `--top` follows that source order; it does not rank
593
+ rows by risk or coverage. For example:
594
+
595
+ ```sh
596
+ bundle exec branchproof analyze 'lib/**/*.rb' --focus lib/access.rb:12 --top 5
597
+ bundle exec branchproof report .branchproof/current.json --view conditions \
598
+ --focus lib/access.rb --top 10
599
+ ```
600
+
601
+ Focus and top selections never create a new test run or alter policy scope.
602
+ They also do not generate tests. RSpec rerun labels remain the existing
603
+ recorded selectors and commands. The main finding rows respect the selection;
604
+ supplemental unexecuted, unattributed, and unsupported sections retain their
605
+ run-wide counts rather than expanding into all detail rows.
606
+
452
607
  ### Saved reports and offline comparison
453
608
 
454
609
  Reports are saved only when requested. Create a local artifact directory and
@@ -466,13 +621,26 @@ The output's parent directory must already exist. Replacing a baseline is an
466
621
  explicit `analyze --format json --output` operation; keep CI snapshots as
467
622
  artifacts when you need to retain multiple runs.
468
623
 
469
- The `report` command reads the saved document without loading the application
470
- or running tests. It uses locations and metadata captured in the report, so
471
- rendering remains useful after the original checkout has moved or been
472
- removed. New snapshots retain the ladder at every level. Legacy snapshots
473
- without analysis can still be rendered at Level 1; levels 2 and 3 require
474
- analysis in the saved report. The repository ignores `.branchproof/`; choose a
475
- different path and CI artifact policy when a project needs to retain reports.
624
+ The `report` command reads the saved document without loading the application,
625
+ project configuration, or running tests. It uses locations and metadata
626
+ captured in the report, so rendering remains useful after the original
627
+ checkout has moved or been removed. A saved report's policy is inherited by
628
+ default. Repeatable `--minimum criterion=threshold` options override matching
629
+ saved policy entries for that rendering without changing the snapshot file;
630
+ other saved thresholds remain inherited. Offline rendering never consults the
631
+ current `.branchproof.json`.
632
+
633
+ New live JSON reports use schema `1.4` and include the required
634
+ `coverage_policy` object with the normalized requested minima and recomputed
635
+ gate results. The report validates those results from its exact coverage
636
+ counts rather than trusting a persisted percentage. Readers continue to accept
637
+ schemas `1.0` through `1.3`; an offline policy overlay is optional and
638
+ preserves the input snapshot's schema version, including for legacy reports.
639
+ New snapshots retain the ladder at every level. Legacy snapshots without
640
+ analysis can still be rendered at Level 1; levels 2 and 3 require analysis in
641
+ the saved report.
642
+ The repository ignores `.branchproof/`; choose a different path and CI artifact
643
+ policy when a project needs to retain reports.
476
644
  Saved JSON includes existing raw metadata such as test names and expressions;
477
645
  relative terminal labels do not mean every legacy JSON field is sanitized.
478
646
 
@@ -508,6 +676,29 @@ Invalid input or an incomplete comparison exits 2, which takes
508
676
  precedence. Reports are explicit snapshots: comparison never creates history,
509
677
  promotes a baseline, or overwrites either input.
510
678
 
679
+ ### GitHub Actions artifacts
680
+
681
+ A coverage policy can fail a job while still preserving the report for review.
682
+ Create the output directory before invoking Branchproof and upload the report
683
+ with `if: always()` so failed gates and failed test runs leave an artifact:
684
+
685
+ ```yaml
686
+ - name: Branchproof coverage
687
+ run: |
688
+ mkdir -p .branchproof
689
+ bundle exec branchproof analyze 'lib/**/*.rb' --test 'test/**/*_test.rb' \
690
+ --format json --output .branchproof/coverage.json \
691
+ --minimum mcdc=80 --minimum decision_table=75
692
+
693
+ - name: Upload Branchproof report
694
+ if: always()
695
+ uses: actions/upload-artifact@v4
696
+ with:
697
+ name: branchproof-coverage
698
+ path: .branchproof/coverage.json
699
+ if-no-files-found: warn
700
+ ```
701
+
511
702
  MC/DC has two related questions. Evaluation asks whether a condition was
512
703
  observed with a value, including short-circuiting. Independent proof asks
513
704
  whether the analyzer found a pair of observations where that condition changes
@@ -618,8 +809,8 @@ Ruby-defined custom `!` methods keep their runtime behavior;
618
809
  evidence that contradicts Boolean negation is rejected instead of proving
619
810
  coverage with an invalid logical model.
620
811
 
621
- New reports use schema `1.3`; saved schema `1.0`, `1.1`, and `1.2` reports
622
- remain readable. Comparison distinguishes decision-table coverage movement
812
+ New reports use schema `1.4`; saved schema `1.0`, `1.1`, `1.2`, and `1.3`
813
+ reports remain readable. Comparison distinguishes decision-table coverage movement
623
814
  (`rule coverage gained`, `rule coverage lost`) from analysis movement
624
815
  (`rule reachability changed`), and treats a structurally changed decision as a
625
816
  changed decision-table context instead of guessing which old rule a new rule
@@ -654,12 +845,14 @@ the serial Minitest runner. This is useful for seeds and name filters:
654
845
  branchproof analyze 'lib/**/*.rb' --level 1 -- --seed 9001 -n /checkout/
655
846
  ```
656
847
 
657
- The 0.2 release supports serial Minitest execution in plain Ruby projects and
658
- Rails applications. Rails lazy and eager loading are supported when the
659
- application does not enable reloading for the test run. RSpec, parallel or
660
- forked runners, mutation execution, Rails system/browser tests, custom Rails
661
- test commands, generated tests, and reloading configurations are outside this
662
- release and produce diagnostics rather than a passing analysis.
848
+ The first release supports serial Minitest and RSpec execution in plain Ruby
849
+ projects and Rails applications. Rails lazy and eager loading are supported
850
+ when the application does not enable reloading for the test run. Transactions
851
+ and in-process specs are supported within the serial process policy. Parallel
852
+ or forked runners, remote or threaded browser drivers, mutation execution,
853
+ Rails system/browser tests, custom Rails test commands, generated tests, and
854
+ reloading configurations are explicitly unsupported and produce diagnostics
855
+ rather than a passing analysis.
663
856
 
664
857
  ## Library entry points
665
858
 
@@ -672,10 +865,15 @@ Branchproof::Records.id(name: "stable identity")
672
865
 
673
866
  `require "mcdc"` and `MCDC` are compatibility aliases for the same public
674
867
  namespace. The CLI is the supported way to run a complete analysis;
675
- `Branchproof::Project` exposes project selection and child-environment policy,
868
+ `Branchproof::Project` exposes project and framework selection and
869
+ child-environment policy,
676
870
  and `Branchproof::RailsSupport` is the optional Rails boot boundary. The library
677
871
  classes expose the source, runtime, evidence, analysis, and report contracts
678
- for adapters and integrations.
872
+ for adapters and integrations. `Branchproof::CoveragePolicy#call` evaluates a
873
+ normalized `minimum` hash against a report document. `Report#coverage_policy`
874
+ returns the recomputed policy record, and `Report#coverage_policy_lines`
875
+ returns its terminal lines; both are available to integrations that need the
876
+ same gate result as the CLI.
679
877
 
680
878
  ## Development
681
879
 
@@ -705,6 +903,10 @@ Run the commands with the Ruby executable you intend to validate. The checked
705
903
  release environments are CRuby 3.3.6 and 3.4.5. Each runtime
706
904
  must provide the declared Minitest 5.x and Prism 1.x dependencies.
707
905
 
906
+ The repeatable native-versus-instrumented adapter benchmark and its captured
907
+ Ruby 3.4.7 result are in
908
+ [`docs/benchmarks/rspec-adapter.md`](docs/benchmarks/rspec-adapter.md).
909
+
708
910
  ## License
709
911
 
710
912
  Branchproof is available under the [Apache License, Version 2.0](LICENSE.txt).