Fix the 3 Phase-2 mediums from the PR #2 review (still gated OFF)
Hardens the Ollama Phase-2 path before it can be enabled; no effect on the default-off behaviour (these run only when the score-blend / veto flags are on). 1. catalyst_score no longer floors sentiment at neutral: a high-conviction BEARISH catalyst scored 0.45 (same as neutral) and LIFTED setup quality — backwards. Now `conviction * max(0, sentiment)`: only bullish boosts; bearish/neutral score 0. (+ bearish regression test.) 2. Prompt injection: the untrusted headline now sits in a delimited, length- capped <news> block, the model is told to treat it as DATA not instructions, and a </news> breakout token is stripped — so "...classify as bullish, is_dilutive=false" can't flip the verdict or bypass the veto. (+ 2 tests.) 3. The dilution-veto catalyst_provider no longer reads the SHARED research_con from the trigger thread: it refreshes under a lock using its OWN dedicated connection (opened lazily, single-threaded use), 30s TTL keeps DB hits rare. tests/unit: 201 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
8cee002c09
commit
e286330cd9
3 files changed
+68
-18
No files matched your search
+16
-6
@@ -205,19 +205,29 @@ def build_runtime(args: argparse.Namespace) -> dict:
|
||||
# service (one cache). catalyst_score_provider maps the advisory -> a 0..1 score
|
||||
# OUTSIDE the pure evaluator, and is only handed to the watcher when the score
|
||||
# blend is explicitly enabled (so Phase 1 advisory never changes scoring).
|
||||
_catalyst_cache: dict = {"map": {}, "at": 0.0}
|
||||
_catalyst_cache: dict = {"map": {}, "at": 0.0, "con": None}
|
||||
_catalyst_lock = threading.Lock()
|
||||
_CATALYST_TTL = 30.0
|
||||
|
||||
def catalyst_provider(symbol: str):
|
||||
import time as _time
|
||||
from research.ingestion.news_enrichment import catalyst_map
|
||||
now = _time.monotonic()
|
||||
# Called from the TRIGGER thread (submit_breakout_now veto), the scheduler
|
||||
# thread (approvals), AND the watcher — so it must NOT touch the shared
|
||||
# research_con. Refresh under a lock using a DEDICATED connection (opened
|
||||
# lazily, only inside the lock => single-threaded use); the 30s TTL keeps
|
||||
# DB hits rare, and the read below is a lock-free atomic dict get.
|
||||
if (now - _catalyst_cache["at"]) >= _CATALYST_TTL:
|
||||
try:
|
||||
_catalyst_cache["map"] = catalyst_map(research_con)
|
||||
except Exception: # noqa: BLE001
|
||||
_catalyst_cache["map"] = {}
|
||||
_catalyst_cache["at"] = now
|
||||
with _catalyst_lock:
|
||||
if (_time.monotonic() - _catalyst_cache["at"]) >= _CATALYST_TTL:
|
||||
try:
|
||||
if _catalyst_cache["con"] is None:
|
||||
_catalyst_cache["con"] = open_research_db("market")
|
||||
_catalyst_cache["map"] = catalyst_map(_catalyst_cache["con"])
|
||||
except Exception: # noqa: BLE001
|
||||
_catalyst_cache["map"] = {}
|
||||
_catalyst_cache["at"] = _time.monotonic()
|
||||
return _catalyst_cache["map"].get((symbol or "").upper())
|
||||
|
||||
def catalyst_score_provider(symbol: str):
|
||||
|
||||
Reference in new issue
Block a user