Module: Rigor::BleedingEdge

Defined in:
lib/rigor/bleeding_edge.rb

Overview

ADR-50 § WD2 — the bleeding-edge overlay.

A Rigor-maintained set of the *next major’s* queued changes —severity-map promotions and new-discipline rule enablements — that a user can adopt early, before they become default-on at a major (ADR-50 § WD7). It is orthogonal to ‘severity_profile:` (how loud *today’s* rules are) and is versioned with the gem, NOT a user-supplied file: the inspectable counterpart to PHPStan’s ‘bleedingEdge` include.

The overlay is **empty today** — no discipline has yet been queued for the next major. This module is the WD2 foundation (the v0.1.19 slice): the surface (‘bleeding_edge:` config, the `rigor show-bleedingedge` command, the severity-composition hook in Configuration::SeverityProfile.resolve) exists and is wired end-to-end, so the first real feature lands as a single FEATURES entry with no engine plumbing.

Each feature carries a **stable feature id** — part of the ADR-50 WD1 contract vocabulary: the config, the ‘show` command, and the eventual CHANGELOG migration note all name the same id, and a feature graduates to default-on at a major by being removed from FEATURES.

Defined Under Namespace

Classes: Feature

Constant Summary collapse

FEATURES =

The overlay. Empty until the first next-major discipline is queued; add a Feature here (with a stable id) when one is.

[].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#idString

Returns the stable feature id (contract vocabulary).

Returns:

  • (String)

    the stable feature id (contract vocabulary).



39
40
41
42
43
44
45
46
47
# File 'lib/rigor/bleeding_edge.rb', line 39

Feature = Data.define(:id, :summary, :severity_overrides) do
  def to_h
    {
      "id" => id,
      "summary" => summary,
      "severity_overrides" => severity_overrides.transform_values(&:to_s)
    }
  end
end

#severity_overridesHash{String => Symbol}

Returns canonical rule id → the severity this feature imposes. Composed below the user’s own ‘severity_overrides:` and above the active `severity_profile` (see Configuration::SeverityProfile.resolve).

Returns:

  • (Hash{String => Symbol})

    canonical rule id → the severity this feature imposes. Composed below the user’s own ‘severity_overrides:` and above the active `severity_profile` (see Configuration::SeverityProfile.resolve).



39
40
41
42
43
44
45
46
47
# File 'lib/rigor/bleeding_edge.rb', line 39

Feature = Data.define(:id, :summary, :severity_overrides) do
  def to_h
    {
      "id" => id,
      "summary" => summary,
      "severity_overrides" => severity_overrides.transform_values(&:to_s)
    }
  end
end

#summaryString

Returns a one-line description of what it changes.

Returns:

  • (String)

    a one-line description of what it changes.



39
40
41
42
43
44
45
46
47
# File 'lib/rigor/bleeding_edge.rb', line 39

Feature = Data.define(:id, :summary, :severity_overrides) do
  def to_h
    {
      "id" => id,
      "summary" => summary,
      "severity_overrides" => severity_overrides.transform_values(&:to_s)
    }
  end
end

Class Method Details

.active_features(selector) ⇒ Array<Feature>

Resolves a normalized ‘bleeding_edge:` selector (see Configuration#bleeding_edge) to the active Feature list. Unknown ids in a `list` / `except` selector are simply absent from the overlay and contribute nothing — symmetric with how `severity_overrides:` keeps an unknown rule id inert until it lands (robust across gem versions).

Parameters:

  • selector (Hash)

    ‘{ “mode” => “none” }`, `{ “mode” => “all” }`, `{ “mode” => “all”, “except” => [ids] }`, or `{ “mode” => “list”, “ids” => [ids] }`.

Returns:



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rigor/bleeding_edge.rb', line 82

def active_features(selector)
  case selector["mode"]
  when "all"
    except = selector["except"] || []
    FEATURES.reject { |f| except.include?(f.id) }
  when "list"
    ids = selector["ids"] || []
    FEATURES.select { |f| ids.include?(f.id) }
  else
    []
  end
end

.feature(id) ⇒ Feature?

Parameters:

  • id (String)

Returns:



67
68
69
# File 'lib/rigor/bleeding_edge.rb', line 67

def feature(id)
  FEATURES.find { |f| f.id == id }
end

.feature_idsArray<String>

Returns every feature id in the overlay.

Returns:

  • (Array<String>)

    every feature id in the overlay.



61
62
63
# File 'lib/rigor/bleeding_edge.rb', line 61

def feature_ids
  FEATURES.map(&:id)
end

.featuresArray<Feature>

Returns the whole overlay.

Returns:

  • (Array<Feature>)

    the whole overlay.



56
57
58
# File 'lib/rigor/bleeding_edge.rb', line 56

def features
  FEATURES
end

.severity_overrides_for(selector) ⇒ Hash{String => Symbol}

The merged severity-override map the active features impose for a selector. Frozen so the result is ‘Ractor.shareable?`.

Parameters:

  • selector (Hash)

    see #active_features.

Returns:

  • (Hash{String => Symbol})


100
101
102
103
104
# File 'lib/rigor/bleeding_edge.rb', line 100

def severity_overrides_for(selector)
  active_features(selector).each_with_object({}) do |feature, acc|
    acc.merge!(feature.severity_overrides)
  end.freeze
end

.unknown_selected_ids(selector) ⇒ Array<String>

Feature ids named by a selector that are NOT in the overlay (typo / graduated / from a newer gem). Surfaced by ‘rigor show-bleedingedge` as a hint; never an error.

Parameters:

  • selector (Hash)

    see #active_features.

Returns:

  • (Array<String>)


112
113
114
115
116
117
118
119
120
121
# File 'lib/rigor/bleeding_edge.rb', line 112

def unknown_selected_ids(selector)
  named =
    case selector["mode"]
    when "list" then selector["ids"] || []
    when "all"  then selector["except"] || []
    else []
    end
  known = feature_ids
  named.reject { |id| known.include?(id) }
end