Class: Rigor::Effects::SnapshotDiff
- Inherits:
-
Object
- Object
- Rigor::Effects::SnapshotDiff
- Defined in:
- lib/rigor/effects/snapshot_diff.rb
Overview
Compares a committed Snapshot with the one this run computes, and judges the difference (ADR-103 WD7; the event vocabulary is design note § 9.4).
The comparison itself is exact — drift between two observations by the same tool is 100 % precise. What is judged is whether the drift matters, and that has two knobs, applied here and never while writing the record:
- the gate.
symmetric(the default) fails on any drift, thedb/schema.rbmodel: a removal is news too, because a job that stopped enqueueing is a bug rather than an improvement.additionsis the ratchet: only growth fails. - the tolerated set. An event whose labels are all admitted by
effects.tolerated:is reported under its own heading and does not fail the gate unlessstrict_tolerated:is set.
Additions are judged per origin; removals by label. The file is flat — origins are explain's
job and a record keyed by them would churn on every refactor — but the current side of a
comparison is a live effect table, so the caller can hand over undischarged:, the labels that
survive per-origin discharge for each current symbol (#385). An added label is then tolerated
exactly when every origin that introduces it is discharged, which is the same answer the envelope
check gives, rather than the coarser "the label is on the list". A removal has no current-side
origins to consult — the thing that produced it is gone — so it stays judged by label. Without the
index (a caller that has no table, and every parse-only spec) both fall back to the label reading.
A header mismatch is neither: it is a regeneration event — the record was written by a different
Rigor, a different vocabulary or a different effects: block, so the two sides are not comparable
in the first place. It fails under both gates, because there is nothing to ratchet against.
Defined Under Namespace
Classes: Event
Constant Summary collapse
- MISSING_SNAPSHOT =
Categories, in the order a text rendering emits them. The strings are the JSON contract.
"missing-snapshot"- REGENERATION =
"regeneration"- SYMBOL_ADDED =
"symbol-added"- SYMBOL_REMOVED =
"symbol-removed"- LABEL_ADDED =
"label-added"- LABEL_REMOVED =
"label-removed"- DECLARED_ADDED =
"declared-added"- DECLARED_REMOVED =
"declared-removed"- MATERIALISED =
"materialised"- EXHAUSTIVE_LOST =
"exhaustive-lost"- EXHAUSTIVE_GAINED =
"exhaustive-gained"- ADDITIVE_CATEGORIES =
What the
additionsratchet still fails on: growth in either lane, a new symbol, and a method that stopped being exhaustive (someone introduced a call the analyzer cannot follow — the one "removal-shaped" change that is unambiguously a loss of knowledge). [SYMBOL_ADDED, LABEL_ADDED, DECLARED_ADDED, EXHAUSTIVE_LOST, MISSING_SNAPSHOT, REGENERATION].freeze
- TABLES =
%w[methods reach].freeze
Instance Attribute Summary collapse
-
#current ⇒ Object
readonly
Returns the value of attribute current.
-
#events ⇒ Object
readonly
Returns the value of attribute events.
-
#gate ⇒ Object
readonly
Returns the value of attribute gate.
-
#recorded ⇒ Object
readonly
Returns the value of attribute recorded.
Class Method Summary collapse
-
.compare(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false, undischarged: nil) ⇒ Object
Compares
currentagainstrecorded.
Instance Method Summary collapse
-
#drift? ⇒ Boolean
Whether
rigor effects checkexits non-zero. - #events_for(table) ⇒ Object
-
#footer ⇒ Object
Renames are a removal plus an addition and are never reported as a lost effect; the footer is where the reviewer sees that the two counts balance.
-
#fresh? ⇒ Boolean
Whether the record matches what this run computed.
-
#initialize(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false, undischarged: nil) ⇒ SnapshotDiff
constructor
A new instance of SnapshotDiff.
- #tolerated_events ⇒ Object
Constructor Details
#initialize(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false, undischarged: nil) ⇒ SnapshotDiff
Returns a new instance of SnapshotDiff.
100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 100 def initialize(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false, undischarged: nil) @recorded = recorded @current = current @gate = gate @strict_tolerated = strict_tolerated @tolerated = LabelSet.new(tolerated) @undischarged = undischarged @added_symbols = 0 @removed_symbols = 0 @events = build_events.freeze freeze end |
Instance Attribute Details
#current ⇒ Object (readonly)
Returns the value of attribute current.
98 99 100 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 98 def current @current end |
#events ⇒ Object (readonly)
Returns the value of attribute events.
98 99 100 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 98 def events @events end |
#gate ⇒ Object (readonly)
Returns the value of attribute gate.
98 99 100 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 98 def gate @gate end |
#recorded ⇒ Object (readonly)
Returns the value of attribute recorded.
98 99 100 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 98 def recorded @recorded end |
Class Method Details
.compare(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false, undischarged: nil) ⇒ Object
Compares current against recorded. recorded may be nil — no snapshot on disk at all, which
is drift with a routed message rather than an error.
92 93 94 95 96 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 92 def self.compare(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false, undischarged: nil) new(recorded: recorded, current: current, tolerated: tolerated, gate: gate, strict_tolerated: strict_tolerated, undischarged: undischarged) end |
Instance Method Details
#drift? ⇒ Boolean
Whether rigor effects check exits non-zero.
121 122 123 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 121 def drift? @events.any? { |event| gating?(event) } end |
#events_for(table) ⇒ Object
131 132 133 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 131 def events_for(table) @events.select { |event| event.table == table && !event.tolerated? } end |
#footer ⇒ Object
Renames are a removal plus an addition and are never reported as a lost effect; the footer is where the reviewer sees that the two counts balance.
127 128 129 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 127 def { added_symbols: @added_symbols, removed_symbols: @removed_symbols } end |
#fresh? ⇒ Boolean
Whether the record matches what this run computed. A tolerated-only difference is NOT fresh — the file still needs regenerating — it merely does not fail the gate.
116 117 118 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 116 def fresh? @events.empty? end |
#tolerated_events ⇒ Object
135 136 137 |
# File 'lib/rigor/effects/snapshot_diff.rb', line 135 def tolerated_events @events.select(&:tolerated?) end |