Class: Ecoportal::API::GraphQL::Diff::Pairing::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/ecoportal/api/graphql/diff/pairing/engine.rb

Overview

The equivalence-matching engine for CROSS-OBJECT pairing (UAT<->PROD, page<->template).

Given two lists of field docs living in different id-spaces, it proposes pairings by:

  1. consulting the LEDGER first — a previously-confirmed pair auto-resolves (method :ledger, confidence 1.0), so pairing improves over time and only novelty is scored;
  2. otherwise scoring every remaining source×target candidate with multi-signal confidence (genome + type + label + options — see Signals), picking the best target per source in a stable, greedy, one-to-one assignment (highest scores first).

It then CLASSIFIES each best candidate:

  • accepted — score >= accept_threshold: high confidence, auto-paired;
  • ambiguous — accept > score >= review_threshold, OR the top two candidates are within tie_margin (genuinely close): route to a HUMAN to adjudicate;
  • unmatched — no target scored >= review_threshold: escalate as a novelty.

NEVER guesses: only accepted pairs are safe to auto-apply / auto-record; ambiguous and unmatched are surfaced for human resolution. Confirmed decisions are written back to the ledger by the caller (or via #confirm!) so the next run needs no human on them.

Defined Under Namespace

Classes: Result

Constant Summary collapse

DEFAULTS =
{ accept_threshold: 0.85, review_threshold: 0.5, tie_margin: 0.1 }.freeze

Instance Method Summary collapse

Constructor Details

#initialize(ledger: nil, kind: :field, **thresholds) ⇒ Engine

Returns a new instance of Engine.

Parameters:

  • ledger (Ledger, nil) (defaults to: nil)

    consulted first + written to on #confirm!.

  • kind (Symbol) (defaults to: :field)

    entity kind recorded in the ledger (default :field).



44
45
46
47
48
# File 'lib/ecoportal/api/graphql/diff/pairing/engine.rb', line 44

def initialize(ledger: nil, kind: :field, **thresholds)
  @ledger = ledger
  @kind   = kind
  @cfg    = DEFAULTS.merge(thresholds)
end

Instance Method Details

#confirm!(candidate, matched_by: nil) ⇒ Object

Persist a confirmed pairing to the ledger (auto-accept or human decision). No-op without a ledger. matched_by overrides the candidate's dominant signal (e.g. :human when a person adjudicated). Returns the recorded Entry (or nil).



63
64
65
66
67
68
69
70
71
# File 'lib/ecoportal/api/graphql/diff/pairing/engine.rb', line 63

def confirm!(candidate, matched_by: nil)
  return nil unless @ledger

  @ledger.record(
    kind: @kind, source_id: candidate.source_id, target_id: candidate.target_id,
    matched_by: matched_by || candidate.matched_by, confidence: candidate.score,
    signals: candidate.signals
  )
end

#pair(sources, targets) ⇒ Object

Pair sources (id-space A) to targets (id-space B). Returns a Result.



51
52
53
54
55
56
57
58
# File 'lib/ecoportal/api/graphql/diff/pairing/engine.rb', line 51

def pair(sources, targets)
  sources = Array(sources)
  targets = Array(targets)

  accepted, remaining_sources, remaining_targets = apply_ledger(sources, targets)
  scored = score_all(remaining_sources, remaining_targets)
  assign(scored, remaining_sources, accepted)
end