Bug Report
Issue
In loopgain/funnel.py, the note_outcome() and note_adapter() methods check self._mode != _ENABLED but _mode is None until _ensure_loaded() is called. If these methods are called before on_init() or on_first_observe(), they incorrectly return early even when telemetry is enabled.
Location
loopgain/funnel.py lines ~630-660 (note_outcome and note_adapter methods)
Details
def note_outcome(self, state: str) -> None:
"""Record a terminal loop state into the coarse outcome distribution."""
if self._mode != _ENABLED: # BUG: _mode is None until _ensure_loaded() called
return
bucket = _STATE_TO_OUTCOME.get(state, "other")
with self._lock:
self._outcomes[bucket] = self._outcomes.get(bucket, 0) + 1
def note_adapter(self, name: Optional[str]) -> None:
"""Record which framework adapter is driving the loop."""
if self._mode != _ENABLED or not name: # BUG: same issue
return
self._adapter = str(name)
The Bug: self._mode is initialized as None in __init__ and only set by _ensure_loaded() (called from on_init() or on_first_observe()). If a framework adapter calls note_adapter() or note_outcome() before the first LoopGain construction triggers on_init(), the check fails and the telemetry data is lost.
Call Sequence That Triggers Bug
- Framework adapter initializes and calls
note_adapter("langgraph")
- User creates
LoopGain instance (triggers on_init())
- User runs loop, calls
observe() (triggers on_first_observe())
- Loop ends, calls
note_outcome("TARGET_MET")
Steps 1 and 4 fail silently because _mode is still None.
Suggested Fix
Call _ensure_loaded() at the start of both methods:
def note_outcome(self, state: str) -> None:
if self._ensure_loaded() != _ENABLED:
return
bucket = _STATE_TO_OUTCOME.get(state, "other")
with self._lock:
self._outcomes[bucket] = self._outcomes.get(bucket, 0) + 1
def note_adapter(self, name: Optional[str]) -> None:
if self._ensure_loaded() != _ENABLED or not name:
return
self._adapter = str(name)
Impact
- Medium: Loss of adapter identification and outcome distribution data for affected runs
- Affects any framework adapter that identifies itself before the first
LoopGain construction
- Silent failure — no error, just missing telemetry
Related
The on_init() and on_first_observe() methods correctly call _ensure_loaded(), but the other public hooks don't.
Bug Report
Issue
In
loopgain/funnel.py, thenote_outcome()andnote_adapter()methods checkself._mode != _ENABLEDbut_modeisNoneuntil_ensure_loaded()is called. If these methods are called beforeon_init()oron_first_observe(), they incorrectly return early even when telemetry is enabled.Location
loopgain/funnel.pylines ~630-660 (note_outcomeandnote_adaptermethods)Details
The Bug:
self._modeis initialized asNonein__init__and only set by_ensure_loaded()(called fromon_init()oron_first_observe()). If a framework adapter callsnote_adapter()ornote_outcome()before the firstLoopGainconstruction triggerson_init(), the check fails and the telemetry data is lost.Call Sequence That Triggers Bug
note_adapter("langgraph")LoopGaininstance (triggerson_init())observe()(triggerson_first_observe())note_outcome("TARGET_MET")Steps 1 and 4 fail silently because
_modeis stillNone.Suggested Fix
Call
_ensure_loaded()at the start of both methods:Impact
LoopGainconstructionRelated
The
on_init()andon_first_observe()methods correctly call_ensure_loaded(), but the other public hooks don't.