ui_guardrails 1.3.0 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a78df8763f35470a40ab6c8c8dcc6daf1351d35e733772a33afde5d1b7af1a54
4
- data.tar.gz: a1f8aa3366f9a350673c55ff4200716e6c37db2f777592b914ada3236f67682e
3
+ metadata.gz: da306d3808653d6bd263a867d1ba0aaf159a2c3a515a6e1284276cc69e34cc8c
4
+ data.tar.gz: c895cef92d7cc7acf0be8ddf97079a51a9753aa434223fc3ce3e5fe28125cc39
5
5
  SHA512:
6
- metadata.gz: f16927ab59ea1e9e89fe51203d3a0ca0f14331956fdcce4ca1e957642579129ba143651d066d9bb763f85fb4b2f3e5afd93a657a9266550b8435e0159478c659
7
- data.tar.gz: 7f4dfebecd72b184a84258ca90f48f978c603eb01739d3217f8cad8b24d0ab7991f2d8a79dc53834e7e67002b0da74219cf68e3aee0ca60c344febb1e3e722e0
6
+ metadata.gz: 4c250a84dfd1ae1ce7ccf39e9e617ede812facb0dfa529bf53ef7bfe2ffd8812327d47409bbaafa4b21a712ae32a6d565ef9fa2c83b5802314793375f5b3208f
7
+ data.tar.gz: 6e97b1ec55de42f6083ee6590c85599445e310d0a8c9bc8d12bce1dec41f2c3becadb1a9955b4b799112e52b754657f0b6bdb0fe3bf3db036a119b0278d8eea7
data/README.md CHANGED
@@ -4,7 +4,7 @@ A Rails toolset that prevents UI drift in AI-assisted applications. Static audit
4
4
 
5
5
  Built and maintained by [Meticulous](https://meticulous.com).
6
6
 
7
- **Current release:** 1.3.0 — V0 + V1 + V2 complete, published on [RubyGems.org as `ui_guardrails`](https://rubygems.org/gems/ui_guardrails). The Ruby module stays `Guardrails` (so `require "guardrails"` is unchanged) — only the gem package name on rubygems carries the `ui_` prefix, to clear RubyGems' similarity rule against the unrelated [`guard-rails`](https://rubygems.org/gems/guard-rails) gem. See [`doc/ROADMAP.md`](doc/ROADMAP.md) for status, [`CHANGELOG.md`](CHANGELOG.md) for the full naming rationale.
7
+ **Current release:** 1.4.0 — V0 + V1 + V2 complete, published on [RubyGems.org as `ui_guardrails`](https://rubygems.org/gems/ui_guardrails). The Ruby module stays `Guardrails` (so `require "guardrails"` is unchanged) — only the gem package name on rubygems carries the `ui_` prefix, to clear RubyGems' similarity rule against the unrelated [`guard-rails`](https://rubygems.org/gems/guard-rails) gem. See [`doc/ROADMAP.md`](doc/ROADMAP.md) for status, [`CHANGELOG.md`](CHANGELOG.md) for the full naming rationale.
8
8
 
9
9
  ---
10
10
 
@@ -219,6 +219,7 @@ The editor is `$GUARDRAILS_EDITOR`, then `$VISUAL`, then `$EDITOR`. VS Code-fami
219
219
  |---|---|
220
220
  | `SUGGEST=1` | Write the markdown checklist alongside the text report. |
221
221
  | `APPLY=1` | Auto-fix raw_color + tailwind_arbitrary where tokens match. |
222
+ | `SEVERITY=error` / `SEVERITY=warning` | Severity floor. `error` checks errors only; `warning` drops suggestions; default is everything. Applies to the report, the exit code, JSON, HTML, and the TUI alike. Detectors that can only produce muted findings aren't run. An unrecognized value aborts rather than being ignored. |
222
223
  | `FORMAT=json` | Emit one JSON document to stdout (all other audit output is suppressed). |
223
224
  | `FORMAT=html` / `OUTPUT=path` | Write the self-contained HTML report (default `tmp/guardrails/audit.html`). |
224
225
  | `GUARDRAILS_EDITOR=cmd` | Editor `guardrails:tui` opens locations in. Falls back to `$VISUAL`, then `$EDITOR`. |
@@ -242,6 +243,15 @@ The audit task is a single shell command:
242
243
  run: bundle exec rake guardrails:audit
243
244
  ```
244
245
 
246
+ Adopting Guardrails on an existing codebase usually means a backlog of warnings you can't clear in one PR. Gate on errors now and ratchet down later:
247
+
248
+ ```yaml
249
+ - name: Guardrails audit (errors only)
250
+ run: bundle exec rake guardrails:audit SEVERITY=error
251
+ ```
252
+
253
+ `SEVERITY=error` exits 1 only when there are error-level findings (raw colors, arbitrary Tailwind values, a11y, visual diffs) and skips the warning- and suggestion-tier detectors entirely, so it's also the fastest way to run the audit. The report ends with a line saying what wasn't checked, so a green run can't be mistaken for a clean codebase.
254
+
245
255
  For richer integration:
246
256
 
247
257
  ```yaml
data/doc/ROADMAP.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Guardrails — Roadmap
2
2
 
3
- **Status:** **1.0.0 released on RubyGems.org** — V0 ✅, V1 ✅, V2 ✅ (visual-diff via snap_diff-capybara landed 0.8.0; published as 1.0.0 once the trusted-publisher pipeline merged)
4
- **Last updated:** 2026-05-11
3
+ **Status:** **1.4.0 released on RubyGems.org** — V0 ✅, V1 ✅, V2 ✅ (the originally-planned roadmap, published as 1.0.0). Post-1.0 releases have been about reading the findings rather than producing more of them: inline suggestions (1.1.0), then an interactive browser + HTML report (1.3.0).
4
+ **Last updated:** 2026-09-19
5
5
 
6
6
  ## Context
7
7
 
@@ -116,6 +116,24 @@ This doc tracks shipped vs. planned scope and parks remaining unknowns. V0 + mos
116
116
 
117
117
  ---
118
118
 
119
+ ## Post-1.0 — Report UX
120
+
121
+ The planned roadmap ended at V2. Running the gem on real codebases (Patchvault: 981 findings) showed the next problem wasn't detection, it was *reading* the output.
122
+
123
+ | Item | Status | Notes |
124
+ |---|---|---|
125
+ | Inline suggestions + triage summary | ✅ shipped (1.1.0) | Every finding carries a `→` action; severity-grouped summary at top and bottom; `[error]` / `[warning]` / `[suggest]` tags; TTY-aware ASCII styling, `NO_COLOR` honored. |
126
+ | Ruby 3.4 / railties 7.2 floor | ✅ shipped (1.2.0) | Compatibility-only release. Ruby 3.2/3.3 or Rails 7.1 users stay on 1.1.0. |
127
+ | Findings data layer | ✅ shipped (1.3.0) | `Guardrails::Report::Run` runs the audit once for every front-end; `Report::Finding` / `Location` / `Category` are detector-agnostic finding data. Every detector implements `categories(result)`, sharing its title / suggestion strings with the text report. New detectors only need to implement that to show up in the TUI and HTML report. |
128
+ | Interactive browser (`rake guardrails:tui`) | ✅ shipped (1.3.0) | Roll-up → category → finding → source, regroup by file, live filter, open in `$EDITOR` at the line, re-run. `io/console` only — no new dependencies. If the hand-rolled rendering stops being enough, swapping in a TUI library means replacing `TUI::Screen` + the loop in `tui.rb`; `TUI::State` (navigation) doesn't know about terminals. |
129
+ | HTML report (`FORMAT=html`) | ✅ shipped (1.3.0) | One self-contained file, no server or assets — opens from disk or as a CI artifact. Generated rather than served: a mounted engine was more surface than the problem needed, and the Lookbook panel already covers in-app. |
130
+ | File-grouped view | ✅ shipped (1.3.0) | `tab` in the TUI. Was a 1.1.0 follow-up. |
131
+ | `SEVERITY=error\|warning` floor | ✅ shipped (1.4.0) | One severity floor across text report, exit code, JSON, HTML, and TUI. Skips detectors that can only produce muted findings. Filtered reports say what wasn't checked; unknown values abort. Supersedes the `--strict` flag dropped in V0. |
132
+ | Lookbook panel on the new data layer | not started | The auto-registered panel still renders from its own `ComponentReport`; it could render `Report::Finding`s and pick up suggestions for free. |
133
+ | CI test workflow | not started | Specs only run inside the release workflow, on one Ruby. A PR-time matrix (3.4 + head) would catch floor regressions before tag time. |
134
+
135
+ ---
136
+
119
137
  ## Decisions Captured
120
138
 
121
139
  | Question | Decision |
@@ -5,6 +5,7 @@ require "pathname"
5
5
  require "set"
6
6
  require_relative "report/style"
7
7
  require_relative "report/finding"
8
+ require_relative "report/severity"
8
9
 
9
10
  module Guardrails
10
11
  # Consumes axe-core JSON output and folds the findings into Guardrails'
@@ -39,7 +40,9 @@ module Guardrails
39
40
  # `failing_impacts:` if your rule pack emits custom severities.
40
41
  DEFAULT_FAILING_IMPACTS = %w[minor moderate serious critical].freeze
41
42
 
42
- def initialize(input:, output: $stdout, failing_impacts: DEFAULT_FAILING_IMPACTS, style: nil)
43
+ def initialize(input:, output: $stdout, failing_impacts: DEFAULT_FAILING_IMPACTS, style: nil,
44
+ min_severity: Report::Severity::DEFAULT)
45
+ @min_severity = min_severity
43
46
  @input = input
44
47
  @output = output
45
48
  @failing_impacts = Set.new(failing_impacts.map(&:to_s))
@@ -47,7 +50,7 @@ module Guardrails
47
50
  end
48
51
 
49
52
  def run
50
- findings = parse_input
53
+ findings = parse_input.select { |f| Report::Severity.include?(impact_to_severity(f.impact), @min_severity) }
51
54
  print_report(findings)
52
55
  findings
53
56
  end
@@ -7,6 +7,7 @@ require "yaml"
7
7
  require_relative "erb_parser"
8
8
  require_relative "report/style"
9
9
  require_relative "report/finding"
10
+ require_relative "report/severity"
10
11
 
11
12
  module Guardrails
12
13
  class Audit
@@ -71,7 +72,9 @@ module Guardrails
71
72
  flood-color lighting-color stop-color
72
73
  ].freeze
73
74
 
74
- def initialize(root:, output: $stdout, suggest: false, format: :text, apply: false, style: nil)
75
+ def initialize(root:, output: $stdout, suggest: false, format: :text, apply: false, style: nil,
76
+ min_severity: Report::Severity::DEFAULT)
77
+ @min_severity = min_severity
75
78
  @root = Pathname(root)
76
79
  @output = output
77
80
  @suggest = suggest
@@ -82,7 +85,12 @@ module Guardrails
82
85
  end
83
86
 
84
87
  def run
85
- violations = collect_files.flat_map { |file| scan_file(file) }
88
+ # Filtered before anything else sees them, so the report, the
89
+ # SUGGEST checklist, and the returned list (→ exit code, JSON)
90
+ # all agree on what this run was about.
91
+ violations = collect_files.flat_map { |file| scan_file(file) }.select do |v|
92
+ Report::Severity.include?(SEVERITY_FOR_TYPE.fetch(v.type, :warning), @min_severity)
93
+ end
86
94
  print_report(violations)
87
95
  remaining = @apply ? apply_auto_fixes(violations) : violations
88
96
  write_suggestions(remaining) if @suggest
@@ -107,7 +107,7 @@
107
107
  </header>
108
108
 
109
109
  <main class="wrap">
110
- <p class="meta"><%= pluralize(findings.length, "finding") %> across <%= pluralize(@categories.length, "category", "categories") %> · <span class="mono"><%= h @root %></span> · generated <%= h @generated_at.strftime("%Y-%m-%d %H:%M") %> by ui_guardrails <%= h Guardrails::VERSION %></p>
110
+ <p class="meta"><%= pluralize(findings.length, "finding") %> across <%= pluralize(@categories.length, "category", "categories") %> · <span class="mono"><%= h @root %></span> · generated <%= h @generated_at.strftime("%Y-%m-%d %H:%M") %> by ui_guardrails <%= h Guardrails::VERSION %><% if muted_note %> · <strong><%= h muted_note %></strong><% end %></p>
111
111
 
112
112
  <%- if @categories.empty? -%>
113
113
  <p class="clean">✓ No findings — the audit is clean.</p>
@@ -34,7 +34,10 @@ module Guardrails
34
34
 
35
35
  SEVERITY_LABEL = { error: "Error", warning: "Warning", suggestion: "Suggestion" }.freeze
36
36
 
37
- def initialize(categories:, root:, generated_at: Time.now)
37
+ # `muted` severities the run was told not to check (SEVERITY=).
38
+ # Stated on the page so a filtered report can't pass for a full one.
39
+ def initialize(categories:, root:, generated_at: Time.now, muted: [])
40
+ @muted = muted
38
41
  @categories = categories
39
42
  @root = Pathname(root).expand_path
40
43
  @generated_at = generated_at
@@ -74,6 +77,12 @@ module Guardrails
74
77
  end
75
78
  end
76
79
 
80
+ def muted_note
81
+ return nil if @muted.empty?
82
+
83
+ "#{@muted.map { |s| "#{SEVERITY_LABEL[s].downcase}s" }.join(' and ')} not checked"
84
+ end
85
+
77
86
  def severity_count(severity)
78
87
  findings.count { |f| f.severity == severity }
79
88
  end
@@ -13,6 +13,7 @@ require_relative "../a11y_deep"
13
13
  require_relative "../visual_diff"
14
14
  require_relative "summary"
15
15
  require_relative "finding"
16
+ require_relative "severity"
16
17
  require_relative "../configuration"
17
18
 
18
19
  module Guardrails
@@ -33,11 +34,13 @@ module Guardrails
33
34
  TRUTHY = %w[1 true yes].freeze
34
35
 
35
36
  attr_reader :violations, :stimulus, :similarity, :view_components,
36
- :a11y, :patterns, :classitis, :a11y_deep, :visual_diff
37
+ :a11y, :patterns, :classitis, :a11y_deep, :visual_diff, :min_severity
37
38
 
38
39
  # Builds a Run from the documented env vars (SUGGEST, APPLY,
39
40
  # AXE_JSON, VISUAL_DIFF*, SIMILARITY_THRESHOLD, PATTERN_*,
40
- # CLASSITIS_*).
41
+ # CLASSITIS_*, SEVERITY). Raises ArgumentError on a SEVERITY it
42
+ # doesn't recognize — a typo there would otherwise silently
43
+ # un-gate a CI check.
41
44
  def self.from_env(root:, style: nil, env: ENV)
42
45
  # Visual-diff is opt-in (baselines need deliberate setup). Enabled
43
46
  # when either VISUAL_DIFF=1 is set in the env (sidecar mode) or
@@ -66,7 +69,8 @@ module Guardrails
66
69
  new(root: root, style: style,
67
70
  suggest: truthy?(env["SUGGEST"]), apply: truthy?(env["APPLY"]),
68
71
  axe_json: env["AXE_JSON"], visual_diff: visual_diff_on,
69
- similarity: similarity, patterns: patterns, classitis: classitis)
72
+ similarity: similarity, patterns: patterns, classitis: classitis,
73
+ min_severity: Severity.parse(env["SEVERITY"]))
70
74
  end
71
75
 
72
76
  def self.truthy?(value)
@@ -80,7 +84,9 @@ module Guardrails
80
84
  # uncolored output (what JSON mode wants — the body is discarded).
81
85
  def initialize(root:, style: nil, suggest: false, apply: false,
82
86
  axe_json: nil, visual_diff: false,
83
- similarity: {}, patterns: {}, classitis: {})
87
+ similarity: {}, patterns: {}, classitis: {},
88
+ min_severity: Severity::DEFAULT)
89
+ @min_severity = min_severity
84
90
  @root = Pathname(root)
85
91
  @style = style
86
92
  @suggest = suggest
@@ -94,20 +100,35 @@ module Guardrails
94
100
  end
95
101
 
96
102
  def call
97
- @violations = detect(Audit.new(**common, suggest: @suggest, apply: @apply, format: :text))
98
- @stimulus = detect(StimulusAudit.new(**common))
99
- @similarity = detect(PartialSimilarity.new(**common, **@similarity_opts))
100
- @view_components = detect(ViewComponentAudit.new(**common))
103
+ # Detectors whose findings all sit below the severity floor
104
+ # aren't run at all — SEVERITY=error skips the two slowest
105
+ # (similarity, patterns) rather than computing results to
106
+ # discard. Audit and A11yDeep emit mixed severities, so they
107
+ # always run and filter internally.
108
+ @violations = detect(Audit.new(**common, suggest: @suggest, apply: @apply, format: :text,
109
+ min_severity: @min_severity))
110
+ @stimulus = wanted?(:warning) ? detect(StimulusAudit.new(**common)) : StimulusAudit::Result.new(orphaned: [], dead: [])
111
+ @similarity = wanted?(:suggestion) ? detect(PartialSimilarity.new(**common, **@similarity_opts)) : []
112
+ @view_components = if wanted?(:warning) then detect(ViewComponentAudit.new(**common))
113
+ else ViewComponentAudit::Result.new(missing_previews: [], orphan_slots: [])
114
+ end
101
115
  @a11y = detect(A11yAudit.new(**common))
102
- @patterns = detect(CrossCodebasePatterns.new(**common, **@pattern_opts))
103
- @classitis = detect(ClassItis.new(**common, **@classitis_opts))
104
- @a11y_deep_runner = @axe_json ? A11yDeep.new(input: @axe_json, output: @sink, style: @style) : nil
116
+ @patterns = wanted?(:suggestion) ? detect(CrossCodebasePatterns.new(**common, **@pattern_opts)) : []
117
+ @classitis = wanted?(:suggestion) ? detect(ClassItis.new(**common, **@classitis_opts)) : []
118
+ @a11y_deep_runner = if @axe_json
119
+ A11yDeep.new(input: @axe_json, output: @sink, style: @style, min_severity: @min_severity)
120
+ end
105
121
  @a11y_deep = @a11y_deep_runner ? detect(@a11y_deep_runner) : []
106
122
  @visual_diff_runner = @visual_diff_on ? VisualDiff.new(**common) : nil
107
123
  @visual_diff = @visual_diff_runner ? detect(@visual_diff_runner) : []
108
124
  self
109
125
  end
110
126
 
127
+ # Severities this run didn't look at (empty by default).
128
+ def muted_severities
129
+ Severity.muted(@min_severity)
130
+ end
131
+
111
132
  # Every finding as detector-agnostic data (see Report::Finding),
112
133
  # grouped by category and ordered errors → warnings →
113
134
  # suggestions, biggest category first — the same order the
@@ -208,6 +229,10 @@ module Guardrails
208
229
  result
209
230
  end
210
231
 
232
+ def wanted?(severity)
233
+ Severity.include?(severity, @min_severity)
234
+ end
235
+
211
236
  def common
212
237
  { root: @root, output: @sink, style: @style }
213
238
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "summary"
4
+
5
+ module Guardrails
6
+ module Report
7
+ # The severity floor behind SEVERITY=: "only tell me about findings
8
+ # at least this serious". `:suggestion` (the default) is everything;
9
+ # `:warning` mutes suggestions; `:error` mutes warnings too.
10
+ module Severity
11
+ ORDER = Summary::SEVERITY_ORDER
12
+ DEFAULT = :suggestion
13
+
14
+ NAMES = {
15
+ "error" => :error, "errors" => :error,
16
+ "warning" => :warning, "warnings" => :warning,
17
+ "suggestion" => :suggestion, "suggestions" => :suggestion, "suggest" => :suggestion, "all" => :suggestion
18
+ }.freeze
19
+
20
+ module_function
21
+
22
+ # nil / blank is "not set", not an error — same blank-env
23
+ # tolerance as the VISUAL_DIFF_* vars.
24
+ def parse(value)
25
+ text = value.to_s.strip.downcase
26
+ return DEFAULT if text.empty?
27
+
28
+ NAMES.fetch(text) do
29
+ raise ArgumentError, "SEVERITY=#{value} isn't a severity. Use error, warning, or suggestion."
30
+ end
31
+ end
32
+
33
+ def include?(severity, floor)
34
+ ORDER.index(severity) <= ORDER.index(floor)
35
+ end
36
+
37
+ # The severities a floor hides, most serious first.
38
+ def muted(floor)
39
+ ORDER.reject { |severity| include?(severity, floor) }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -90,7 +90,7 @@ module Guardrails
90
90
  end
91
91
 
92
92
  def open_web_report
93
- path = Report::Html.new(categories: @run.categories, root: @root).write
93
+ path = Report::Html.new(categories: @run.categories, root: @root, muted: @run.muted_severities).write
94
94
  opener = Editor.opener(path.to_s)
95
95
  opened = opener && system(*opener, out: File::NULL, err: File::NULL)
96
96
  @state.notice = "#{opened ? 'Opened' : 'Wrote'} #{path.relative_path_from(Pathname(@root).expand_path)}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Guardrails
4
- VERSION = "1.3.0"
4
+ VERSION = "1.4.0"
5
5
  end
@@ -9,7 +9,7 @@ namespace :guardrails do
9
9
  Guardrails::Init.new(root: root, force: force).run
10
10
  end
11
11
 
12
- desc "Audit views and components for UI drift (SUGGEST=1, APPLY=1, FORMAT=json|html)"
12
+ desc "Audit views and components for UI drift (SUGGEST=1, APPLY=1, FORMAT=json|html, SEVERITY=error|warning)"
13
13
  task :audit do
14
14
  require "guardrails/report/run"
15
15
  require "guardrails/report/style"
@@ -22,7 +22,11 @@ namespace :guardrails do
22
22
  # (see Report::Run). JSON and HTML modes discard the report body,
23
23
  # so they run unstyled.
24
24
  report_style = format == :text ? Guardrails::Report::Style.new(io: $stdout) : nil
25
- run = Guardrails::Report::Run.from_env(root: root, style: report_style).call
25
+ run = begin
26
+ Guardrails::Report::Run.from_env(root: root, style: report_style).call
27
+ rescue ArgumentError => e
28
+ abort e.message
29
+ end
26
30
 
27
31
  if format == :json
28
32
  require "json"
@@ -32,7 +36,7 @@ namespace :guardrails do
32
36
  # CI artifact. OUTPUT= overrides the default tmp/ location.
33
37
  require "guardrails/report/html"
34
38
  output = ENV["OUTPUT"].to_s.strip
35
- path = Guardrails::Report::Html.new(categories: run.categories, root: root)
39
+ path = Guardrails::Report::Html.new(categories: run.categories, root: root, muted: run.muted_severities)
36
40
  .write(output.empty? ? Guardrails::Report::Html::DEFAULT_PATH : output)
37
41
  $stdout.puts "Guardrails audit: #{run.findings.length} findings → #{path}"
38
42
  else
@@ -45,6 +49,13 @@ namespace :guardrails do
45
49
  summary.render
46
50
  $stdout.write run.body
47
51
  summary.render(recap: true)
52
+ # A filtered run that prints "no violations found" must not read
53
+ # as a clean bill of health.
54
+ unless run.muted_severities.empty?
55
+ muted = run.muted_severities.map { |s| "#{s}s" }.join(" and ")
56
+ $stdout.puts ""
57
+ $stdout.puts report_style.colorize("SEVERITY=#{run.min_severity} — #{muted} were not checked.", :dim)
58
+ end
48
59
  end
49
60
 
50
61
  exit 1 if run.failing?
@@ -63,7 +74,7 @@ namespace :guardrails do
63
74
  runner = -> { Guardrails::Report::Run.from_env(root: root, env: env).call }
64
75
  begin
65
76
  Guardrails::TUI.new(runner: runner, root: root).start
66
- rescue Guardrails::TUI::NotInteractive => e
77
+ rescue Guardrails::TUI::NotInteractive, ArgumentError => e
67
78
  abort e.message
68
79
  end
69
80
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ui_guardrails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Athayde
@@ -109,6 +109,7 @@ files:
109
109
  - lib/guardrails/report/html.rb
110
110
  - lib/guardrails/report/html/template.html.erb
111
111
  - lib/guardrails/report/run.rb
112
+ - lib/guardrails/report/severity.rb
112
113
  - lib/guardrails/report/style.rb
113
114
  - lib/guardrails/report/summary.rb
114
115
  - lib/guardrails/stimulus_audit.rb