necropsy 0.2.0 → 0.3.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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/MEASUREMENTS.md +27 -0
  4. data/README.md +182 -17
  5. data/bench/README.md +92 -0
  6. data/bench/audit.rb +113 -0
  7. data/bench/audits/0.2.1/audit.json +501 -0
  8. data/bench/audits/0.2.1/audit.md +55 -0
  9. data/bench/audits/0.2.1/baseline_performance.yml +16 -0
  10. data/bench/audits/0.2.1/config.yml +42 -0
  11. data/bench/audits/0.2.1/review.yml +56 -0
  12. data/bench/audits/0.2.1/review_queue.yml +3651 -0
  13. data/bench/corpora/v1/README.md +30 -0
  14. data/bench/corpora/v1/labels.yml +37 -0
  15. data/bench/corpora/v1/manifest.yml +63 -0
  16. data/bench/corpora/v1/rubocop.necropsy.yml +7 -0
  17. data/bench/corpora/v1/self.necropsy.yml +8 -0
  18. data/bench/corpora/v1/tools/debride.yml +42 -0
  19. data/bench/corpora/v1/tools/spoom.yml +12 -0
  20. data/bench/corpora/v1/tools/type_aware.yml +11 -0
  21. data/bench/golden/v1/candidate_union.json +21918 -0
  22. data/bench/golden/v1/metadata.json +12 -0
  23. data/bench/golden/v1/reports/dynamic_evidence.json +146 -0
  24. data/bench/golden/v1/reports/plain_ruby.json +135 -0
  25. data/bench/golden/v1/reports/rails.json +176 -0
  26. data/bench/golden/v1/reports/rubocop_1_75_0.json +26848 -0
  27. data/bench/golden/v1/reports/self.json +3449 -0
  28. data/bench/review_queue.rb +35 -0
  29. data/bench/run.rb +31 -0
  30. data/bench/schema/candidate-union-v1.schema.json +70 -0
  31. data/docs/impv_implementation_matrix.md +179 -0
  32. data/docs/migrations/0.2.1.md +57 -0
  33. data/docs/migrations/0.3.0.md +207 -0
  34. data/docs/migrations/0.4.0.md +13 -0
  35. data/docs/necropsy_performance_adr.md +43 -0
  36. data/docs/necropsy_scope_decisions.md +58 -0
  37. data/docs/necropsy_type_facts_adr.md +22 -0
  38. data/gemfiles/prism_min.gemfile +9 -0
  39. data/gemfiles/prism_min.gemfile.lock +50 -0
  40. data/lib/necropsy/analyzer.rb +121 -2
  41. data/lib/necropsy/analyzers/dynamic/coverage_collector.rb +69 -17
  42. data/lib/necropsy/analyzers/dynamic/coverage_importer.rb +99 -9
  43. data/lib/necropsy/analyzers/dynamic/coverband_importer.rb +69 -299
  44. data/lib/necropsy/analyzers/dynamic/coverband_payload_set.rb +149 -0
  45. data/lib/necropsy/analyzers/dynamic/observation_policy.rb +96 -0
  46. data/lib/necropsy/analyzers/dynamic/redis_input_limits.rb +121 -0
  47. data/lib/necropsy/analyzers/dynamic/redis_nonblocking_io.rb +94 -0
  48. data/lib/necropsy/analyzers/dynamic/redis_payload_loader.rb +165 -0
  49. data/lib/necropsy/analyzers/dynamic/redis_transport.rb +217 -0
  50. data/lib/necropsy/analyzers/dynamic/runtime_reference.rb +96 -0
  51. data/lib/necropsy/analyzers/dynamic/trace_point_collector.rb +133 -23
  52. data/lib/necropsy/analyzers/dynamic/trace_point_importer.rb +3 -1
  53. data/lib/necropsy/analyzers/legacy_result_adapter.rb +226 -0
  54. data/lib/necropsy/analyzers/static/cha.rb +33 -73
  55. data/lib/necropsy/analyzers/static/name_resolution.rb +133 -12
  56. data/lib/necropsy/analyzers/static/rta.rb +237 -29
  57. data/lib/necropsy/ast_scanner/call_recording.rb +149 -30
  58. data/lib/necropsy/ast_scanner/call_site_creation.rb +54 -0
  59. data/lib/necropsy/ast_scanner/definition_creation.rb +43 -0
  60. data/lib/necropsy/ast_scanner/dsl_macros.rb +431 -41
  61. data/lib/necropsy/ast_scanner/method_definitions.rb +232 -50
  62. data/lib/necropsy/ast_scanner/references.rb +43 -9
  63. data/lib/necropsy/ast_scanner/ruby_semantics.rb +101 -15
  64. data/lib/necropsy/ast_scanner/traversal.rb +234 -71
  65. data/lib/necropsy/ast_scanner/value_definitions.rb +23 -13
  66. data/lib/necropsy/ast_scanner.rb +67 -6
  67. data/lib/necropsy/bench/candidate_union.rb +555 -0
  68. data/lib/necropsy/bench/claim_gate.rb +112 -0
  69. data/lib/necropsy/bench/evaluator.rb +329 -16
  70. data/lib/necropsy/bench/finding_facts.rb +152 -0
  71. data/lib/necropsy/bench/precision_gate.rb +144 -0
  72. data/lib/necropsy/bench/release_audit/adversarial_runner.rb +56 -0
  73. data/lib/necropsy/bench/release_audit/artifact_writer.rb +112 -0
  74. data/lib/necropsy/bench/release_audit/config_validator.rb +112 -0
  75. data/lib/necropsy/bench/release_audit/git_snapshot.rb +36 -0
  76. data/lib/necropsy/bench/release_audit/performance_gate.rb +165 -0
  77. data/lib/necropsy/bench/release_audit/run_provenance.rb +133 -0
  78. data/lib/necropsy/bench/release_audit.rb +360 -0
  79. data/lib/necropsy/bench/report_normalizer.rb +140 -0
  80. data/lib/necropsy/bench/review_queue.rb +154 -0
  81. data/lib/necropsy/bench/safety_mutation_harness.rb +59 -0
  82. data/lib/necropsy/bench/seed_runner.rb +408 -0
  83. data/lib/necropsy/bounded_canonicalizer.rb +218 -0
  84. data/lib/necropsy/cache/scan_cache.rb +85 -17
  85. data/lib/necropsy/call_site_identity.rb +54 -0
  86. data/lib/necropsy/cli.rb +220 -33
  87. data/lib/necropsy/clock.rb +40 -0
  88. data/lib/necropsy/confidence/scorer.rb +103 -58
  89. data/lib/necropsy/configuration.rb +224 -21
  90. data/lib/necropsy/convention_rules.rb +138 -0
  91. data/lib/necropsy/definition_identity/canonical_digest.rb +278 -0
  92. data/lib/necropsy/definition_identity.rb +37 -0
  93. data/lib/necropsy/diagnostics.rb +176 -36
  94. data/lib/necropsy/embedded_ruby.rb +55 -0
  95. data/lib/necropsy/entry_points/plain.rb +111 -10
  96. data/lib/necropsy/entry_points/rails.rb +322 -41
  97. data/lib/necropsy/entry_points/test.rb +6 -1
  98. data/lib/necropsy/flow_interpreter.rb +460 -0
  99. data/lib/necropsy/graph/blocker_matching.rb +338 -0
  100. data/lib/necropsy/graph/call_graph.rb +1099 -109
  101. data/lib/necropsy/graph/definition_index.rb +149 -0
  102. data/lib/necropsy/graph/dynamic_evidence_tracking.rb +206 -0
  103. data/lib/necropsy/graph/evidence_store.rb +213 -0
  104. data/lib/necropsy/graph/resolution_store.rb +497 -0
  105. data/lib/necropsy/graph_self_check.rb +79 -0
  106. data/lib/necropsy/guardrail/baseline.rb +350 -13
  107. data/lib/necropsy/guardrail/quarantine.rb +94 -9
  108. data/lib/necropsy/load_graph.rb +206 -0
  109. data/lib/necropsy/models.rb +878 -13
  110. data/lib/necropsy/performance_profiler.rb +108 -0
  111. data/lib/necropsy/project.rb +327 -25
  112. data/lib/necropsy/reachability/engine.rb +54 -13
  113. data/lib/necropsy/reference_barrier.rb +458 -0
  114. data/lib/necropsy/report.rb +113 -4
  115. data/lib/necropsy/reporter.rb +431 -15
  116. data/lib/necropsy/runner.rb +233 -18
  117. data/lib/necropsy/runtime_feedback.rb +136 -0
  118. data/lib/necropsy/semantics_matrix.rb +153 -0
  119. data/lib/necropsy/type_facts.rb +53 -0
  120. data/lib/necropsy/version.rb +1 -1
  121. data/lib/necropsy/why_not_explanation.rb +436 -0
  122. data/lib/necropsy/why_not_renderer.rb +197 -0
  123. data/lib/necropsy/world_policy.rb +90 -0
  124. data/lib/necropsy.rb +35 -2
  125. data/schema/necropsy-report-v2.schema.json +366 -0
  126. metadata +85 -1
@@ -0,0 +1,12 @@
1
+ {
2
+ "schema_version": 1,
3
+ "update_reason": "Record reviewed high candidate measurement and pending review queue",
4
+ "artifacts": {
5
+ "candidate_union.json": "0b125c5c8be67eb281237d5fe854c6d5d76860cc5de47555ea5ba5a2385a5096",
6
+ "reports/dynamic_evidence.json": "bd82a2b9fb2fae3777d81743917f2df59744798c5f58747219565436134de7ed",
7
+ "reports/plain_ruby.json": "fc939ec706e35ada3c59428856b9f8408ffc3f7468265a3c322c26c65c138169",
8
+ "reports/rails.json": "c0d2195364260ff955ae933acfb57d4e3e9697185e619d79635f70ca0f4b01fb",
9
+ "reports/rubocop_1_75_0.json": "48a136417c6b5802282720cad1f9a608a709b7a8b6e712de2ae228edd79413b5",
10
+ "reports/self.json": "b093ce61f2c7f7646cbdaec1daacbc934d25aa59e7a1ce48bc298e1b5bc35698"
11
+ }
12
+ }
@@ -0,0 +1,146 @@
1
+ {
2
+ "schema_version": 1,
3
+ "corpus": "dynamic_evidence",
4
+ "metrics": {
5
+ "analysis_graph": {
6
+ "scope": "analysis",
7
+ "nodes": 6,
8
+ "definitions": 5,
9
+ "call_sites": 4,
10
+ "edges": 2
11
+ },
12
+ "findings": 4,
13
+ "actionable_candidates": 4,
14
+ "candidate_loc": 12,
15
+ "diagnostic_findings": 0
16
+ },
17
+ "states": {
18
+ "unreachable": 4
19
+ },
20
+ "quality": {
21
+ "scope": "report",
22
+ "candidate_count": 4,
23
+ "candidate_loc": 12,
24
+ "diagnostic_count": 0,
25
+ "blocked_count": 0,
26
+ "blocked_rate": 0.0,
27
+ "unknown_finding_count": 0,
28
+ "unknown_finding_rate": 0.0,
29
+ "resolution_counts": {
30
+ "total": 12,
31
+ "complete": 4,
32
+ "partial": 2,
33
+ "unknown": 6
34
+ },
35
+ "unknown_resolution_rate": 0.5,
36
+ "rule_counts": {},
37
+ "risk_counts": {
38
+ "public_or_protected_visibility": 4
39
+ },
40
+ "by_category": {
41
+ "unreachable": {
42
+ "findings": 4,
43
+ "candidate_count": 4,
44
+ "candidate_loc": 12,
45
+ "blocked_count": 0,
46
+ "blocked_rate": 0.0,
47
+ "unknown_count": 0,
48
+ "unknown_rate": 0.0,
49
+ "rule_counts": {},
50
+ "risk_counts": {
51
+ "public_or_protected_visibility": 4
52
+ }
53
+ }
54
+ }
55
+ },
56
+ "findings": [
57
+ {
58
+ "id": "DynamicSeed#dead",
59
+ "definition_id": "def:v1:462aacae140d5237759b4516e121ed032ed65edd9e9460e5c2c0aaced5d3f05b",
60
+ "path": "lib/dynamic_seed.rb",
61
+ "line": 16,
62
+ "end_line": 18,
63
+ "loc": 3,
64
+ "state": "unreachable",
65
+ "confidence": "medium",
66
+ "candidate": true,
67
+ "diagnostic": false,
68
+ "category": "unreachable",
69
+ "unknown": false,
70
+ "rule_hits": [],
71
+ "risk_flags": [
72
+ "public_or_protected_visibility"
73
+ ],
74
+ "blocker_kinds": [],
75
+ "reasons": [
76
+ "Runtime observations did not include this definition; classification and confidence are unchanged."
77
+ ]
78
+ },
79
+ {
80
+ "id": "DynamicSeed#helper",
81
+ "definition_id": "def:v1:b5057efa66e7850ebbcc22e4c3d19accd0e1537ca2b2f4b6bee7614d3ca8518e",
82
+ "path": "lib/dynamic_seed.rb",
83
+ "line": 8,
84
+ "end_line": 10,
85
+ "loc": 3,
86
+ "state": "unreachable",
87
+ "confidence": "medium",
88
+ "candidate": true,
89
+ "diagnostic": false,
90
+ "category": "unreachable",
91
+ "unknown": false,
92
+ "rule_hits": [],
93
+ "risk_flags": [
94
+ "public_or_protected_visibility"
95
+ ],
96
+ "blocker_kinds": [],
97
+ "reasons": [
98
+ "Runtime observations did not include this definition; classification and confidence are unchanged."
99
+ ]
100
+ },
101
+ {
102
+ "id": "DynamicSeed#public_hook",
103
+ "definition_id": "def:v1:2f73f908d6c20c6c52ae5d106e2f4efda727c0f2227c7fcbd1a2a529990c05f7",
104
+ "path": "lib/dynamic_seed.rb",
105
+ "line": 20,
106
+ "end_line": 22,
107
+ "loc": 3,
108
+ "state": "unreachable",
109
+ "confidence": "medium",
110
+ "candidate": true,
111
+ "diagnostic": false,
112
+ "category": "unreachable",
113
+ "unknown": false,
114
+ "rule_hits": [],
115
+ "risk_flags": [
116
+ "public_or_protected_visibility"
117
+ ],
118
+ "blocker_kinds": [],
119
+ "reasons": [
120
+ "Runtime observations did not include this definition; classification and confidence are unchanged."
121
+ ]
122
+ },
123
+ {
124
+ "id": "DynamicSeed#run",
125
+ "definition_id": "def:v1:62bc0fd1e53699ef877f7dec4e15fc86e37fbeed0a23abd58ee4fe7d34bd010d",
126
+ "path": "lib/dynamic_seed.rb",
127
+ "line": 4,
128
+ "end_line": 6,
129
+ "loc": 3,
130
+ "state": "unreachable",
131
+ "confidence": "medium",
132
+ "candidate": true,
133
+ "diagnostic": false,
134
+ "category": "unreachable",
135
+ "unknown": false,
136
+ "rule_hits": [],
137
+ "risk_flags": [
138
+ "public_or_protected_visibility"
139
+ ],
140
+ "blocker_kinds": [],
141
+ "reasons": [
142
+ "Runtime observations did not include this definition; classification and confidence are unchanged."
143
+ ]
144
+ }
145
+ ]
146
+ }
@@ -0,0 +1,135 @@
1
+ {
2
+ "schema_version": 1,
3
+ "corpus": "plain_ruby",
4
+ "metrics": {
5
+ "analysis_graph": {
6
+ "scope": "analysis",
7
+ "nodes": 8,
8
+ "definitions": 6,
9
+ "call_sites": 7,
10
+ "edges": 3
11
+ },
12
+ "findings": 3,
13
+ "actionable_candidates": 0,
14
+ "candidate_loc": 0,
15
+ "diagnostic_findings": 3
16
+ },
17
+ "states": {
18
+ "blocked": 3
19
+ },
20
+ "quality": {
21
+ "scope": "report",
22
+ "candidate_count": 0,
23
+ "candidate_loc": 0,
24
+ "diagnostic_count": 3,
25
+ "blocked_count": 3,
26
+ "blocked_rate": 1.0,
27
+ "unknown_finding_count": 3,
28
+ "unknown_finding_rate": 1.0,
29
+ "resolution_counts": {
30
+ "total": 21,
31
+ "complete": 6,
32
+ "partial": 3,
33
+ "unknown": 12
34
+ },
35
+ "unknown_resolution_rate": 0.5714,
36
+ "rule_counts": {},
37
+ "risk_counts": {
38
+ "analysis_incomplete": 3,
39
+ "public_or_protected_visibility": 3
40
+ },
41
+ "by_category": {
42
+ "unparsed_external_reference": {
43
+ "findings": 3,
44
+ "candidate_count": 0,
45
+ "candidate_loc": 0,
46
+ "blocked_count": 3,
47
+ "blocked_rate": 1.0,
48
+ "unknown_count": 3,
49
+ "unknown_rate": 1.0,
50
+ "rule_counts": {},
51
+ "risk_counts": {
52
+ "analysis_incomplete": 3,
53
+ "public_or_protected_visibility": 3
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "findings": [
59
+ {
60
+ "id": "PlainSeed::Formatter#public_extension",
61
+ "definition_id": "def:v1:7366747698494278ed20d632c891ee1e5b70d1efd9d592d7715ed51da6741be8",
62
+ "path": "lib/plain_seed.rb",
63
+ "line": 17,
64
+ "end_line": 19,
65
+ "loc": 3,
66
+ "state": "blocked",
67
+ "confidence": "low",
68
+ "candidate": false,
69
+ "diagnostic": true,
70
+ "category": "unparsed_external_reference",
71
+ "unknown": true,
72
+ "rule_hits": [],
73
+ "risk_flags": [
74
+ "analysis_incomplete",
75
+ "public_or_protected_visibility"
76
+ ],
77
+ "blocker_kinds": [
78
+ "unparsed_external_reference"
79
+ ],
80
+ "reasons": [
81
+ "Blocked by 1 unresolved runtime dispatch."
82
+ ]
83
+ },
84
+ {
85
+ "id": "PlainSeed::Formatter#unused",
86
+ "definition_id": "def:v1:f2b441b18942ad826f5d08ae9c1fa8daf2a898c76ba8caa06004c6089c457f55",
87
+ "path": "lib/plain_seed.rb",
88
+ "line": 13,
89
+ "end_line": 15,
90
+ "loc": 3,
91
+ "state": "blocked",
92
+ "confidence": "low",
93
+ "candidate": false,
94
+ "diagnostic": true,
95
+ "category": "unparsed_external_reference",
96
+ "unknown": true,
97
+ "rule_hits": [],
98
+ "risk_flags": [
99
+ "analysis_incomplete",
100
+ "public_or_protected_visibility"
101
+ ],
102
+ "blocker_kinds": [
103
+ "unparsed_external_reference"
104
+ ],
105
+ "reasons": [
106
+ "Blocked by 1 unresolved runtime dispatch."
107
+ ]
108
+ },
109
+ {
110
+ "id": "PlainSeed::Runner#legacy",
111
+ "definition_id": "def:v1:a91ec9a867cc0bf56979fbb89dcbbab1288c8458c63386d5cbd2030f992e6f36",
112
+ "path": "lib/plain_seed.rb",
113
+ "line": 27,
114
+ "end_line": 29,
115
+ "loc": 3,
116
+ "state": "blocked",
117
+ "confidence": "low",
118
+ "candidate": false,
119
+ "diagnostic": true,
120
+ "category": "unparsed_external_reference",
121
+ "unknown": true,
122
+ "rule_hits": [],
123
+ "risk_flags": [
124
+ "analysis_incomplete",
125
+ "public_or_protected_visibility"
126
+ ],
127
+ "blocker_kinds": [
128
+ "unparsed_external_reference"
129
+ ],
130
+ "reasons": [
131
+ "Blocked by 1 unresolved runtime dispatch."
132
+ ]
133
+ }
134
+ ]
135
+ }
@@ -0,0 +1,176 @@
1
+ {
2
+ "schema_version": 1,
3
+ "corpus": "rails",
4
+ "metrics": {
5
+ "analysis_graph": {
6
+ "scope": "analysis",
7
+ "nodes": 32,
8
+ "definitions": 23,
9
+ "call_sites": 49,
10
+ "edges": 17
11
+ },
12
+ "findings": 4,
13
+ "actionable_candidates": 3,
14
+ "candidate_loc": 7,
15
+ "diagnostic_findings": 1
16
+ },
17
+ "states": {
18
+ "test_only_reachable": 1,
19
+ "unreachable": 3
20
+ },
21
+ "quality": {
22
+ "scope": "report",
23
+ "candidate_count": 3,
24
+ "candidate_loc": 7,
25
+ "diagnostic_count": 1,
26
+ "blocked_count": 0,
27
+ "blocked_rate": 0.0,
28
+ "unknown_finding_count": 0,
29
+ "unknown_finding_rate": 0.0,
30
+ "resolution_counts": {
31
+ "total": 147,
32
+ "complete": 22,
33
+ "partial": 22,
34
+ "unknown": 103
35
+ },
36
+ "unknown_resolution_rate": 0.7007,
37
+ "rule_counts": {
38
+ "callback_registered": 1,
39
+ "rails_component": 1,
40
+ "rails_route": 9,
41
+ "rails_view_helper": 2,
42
+ "rails_view_reference": 1
43
+ },
44
+ "risk_counts": {
45
+ "generated_method": 1,
46
+ "public_or_protected_visibility": 4
47
+ },
48
+ "by_category": {
49
+ "generated_method": {
50
+ "findings": 1,
51
+ "candidate_count": 1,
52
+ "candidate_loc": 1,
53
+ "blocked_count": 0,
54
+ "blocked_rate": 0.0,
55
+ "unknown_count": 0,
56
+ "unknown_rate": 0.0,
57
+ "rule_counts": {},
58
+ "risk_counts": {
59
+ "generated_method": 1,
60
+ "public_or_protected_visibility": 1
61
+ }
62
+ },
63
+ "test_only_reachable": {
64
+ "findings": 1,
65
+ "candidate_count": 0,
66
+ "candidate_loc": 0,
67
+ "blocked_count": 0,
68
+ "blocked_rate": 0.0,
69
+ "unknown_count": 0,
70
+ "unknown_rate": 0.0,
71
+ "rule_counts": {},
72
+ "risk_counts": {
73
+ "public_or_protected_visibility": 1
74
+ }
75
+ },
76
+ "unreachable": {
77
+ "findings": 2,
78
+ "candidate_count": 2,
79
+ "candidate_loc": 6,
80
+ "blocked_count": 0,
81
+ "blocked_rate": 0.0,
82
+ "unknown_count": 0,
83
+ "unknown_rate": 0.0,
84
+ "rule_counts": {},
85
+ "risk_counts": {
86
+ "public_or_protected_visibility": 2
87
+ }
88
+ }
89
+ }
90
+ },
91
+ "findings": [
92
+ {
93
+ "id": "Sample::Widget#<=>",
94
+ "definition_id": "def:v1:d785fc9e7140c3c1b0231b4b81f69bef10f93cf597e7edc8fada0bd37aa3ea83",
95
+ "path": "app/models/widget.rb",
96
+ "line": 43,
97
+ "end_line": 45,
98
+ "loc": 3,
99
+ "state": "unreachable",
100
+ "confidence": "low",
101
+ "candidate": true,
102
+ "diagnostic": false,
103
+ "category": "unreachable",
104
+ "unknown": false,
105
+ "rule_hits": [],
106
+ "risk_flags": [
107
+ "public_or_protected_visibility"
108
+ ],
109
+ "blocker_kinds": [],
110
+ "reasons": [
111
+ "Lowered because <=> is a Ruby protocol method invoked implicitly."
112
+ ]
113
+ },
114
+ {
115
+ "id": "Sample::Widget#alias_live",
116
+ "definition_id": "def:v1:116fa7b0d782f572553497fc44cdfba132618efcdbfff2694f9d7b540b4003d2",
117
+ "path": "app/models/widget.rb",
118
+ "line": 37,
119
+ "end_line": 37,
120
+ "loc": 1,
121
+ "state": "unreachable",
122
+ "confidence": "medium",
123
+ "candidate": true,
124
+ "diagnostic": false,
125
+ "category": "generated_method",
126
+ "unknown": false,
127
+ "rule_hits": [],
128
+ "risk_flags": [
129
+ "generated_method",
130
+ "public_or_protected_visibility"
131
+ ],
132
+ "blocker_kinds": [],
133
+ "reasons": []
134
+ },
135
+ {
136
+ "id": "Sample::Widget#dead_model",
137
+ "definition_id": "def:v1:4eb57429e3a250519819576bc125a4271340303473f23bc6d5c0e4192cf89819",
138
+ "path": "app/models/widget.rb",
139
+ "line": 51,
140
+ "end_line": 53,
141
+ "loc": 3,
142
+ "state": "unreachable",
143
+ "confidence": "medium",
144
+ "candidate": true,
145
+ "diagnostic": false,
146
+ "category": "unreachable",
147
+ "unknown": false,
148
+ "rule_hits": [],
149
+ "risk_flags": [
150
+ "public_or_protected_visibility"
151
+ ],
152
+ "blocker_kinds": [],
153
+ "reasons": []
154
+ },
155
+ {
156
+ "id": "Sample::Widget#test_only",
157
+ "definition_id": "def:v1:8a2c86b54d2d1ac0212463c51cff24b81d2e8a46eb5eb3ecd8db86e74911f0eb",
158
+ "path": "app/models/widget.rb",
159
+ "line": 55,
160
+ "end_line": 57,
161
+ "loc": 3,
162
+ "state": "test_only_reachable",
163
+ "confidence": "medium",
164
+ "candidate": false,
165
+ "diagnostic": true,
166
+ "category": "test_only_reachable",
167
+ "unknown": false,
168
+ "rule_hits": [],
169
+ "risk_flags": [
170
+ "public_or_protected_visibility"
171
+ ],
172
+ "blocker_kinds": [],
173
+ "reasons": []
174
+ }
175
+ ]
176
+ }