feat: dream-cycle merge scan + pattern desk (leak recall)
Phases 5 & 6, completing the scouting desk. Phase 5 — the nightly consolidation (dream cycle, coherence block) now runs poker.scan_merge_candidates(), filing likely same-person merges to the review queue off the hot path. Fail-safe: a scan error never sinks the cycle. Phase 6 — "you've hit this leak before". Scar/confidence notes are embedded on write; recall_similar_rituals() finds past ones close to the current spot, excluding tonight. The scouting desk adds a pattern pass that surfaces them — but ONLY on genuine strategy/tilt talk (a length + cue gate), so routine logging like "stack 350" never pays for an embed. Respects the per-turn latency concern. 7 tests (deterministic embed stub to keep threshold assertions stable). Full suite 153 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+33
-5
@@ -23,6 +23,20 @@ _DESC_PATTERNS = (
|
||||
)
|
||||
_MIN_NAME = 3
|
||||
|
||||
# Cues that a message is a strategy/spot/tilt discussion — the only turns worth
|
||||
# paying an embed to recall past leaks. Keeps the pattern pass off routine logging.
|
||||
_STRAT_CUES = (
|
||||
"fold", "call", "raise", "bluff", "river", "turn", "flop", "tilt", "punt",
|
||||
"leak", "should i", "hero", "value", "overbet", "spew", "stack off", "3bet",
|
||||
"4bet", "check-raise", "checkraise", "range", "board", "steaming", "felted",
|
||||
"all in", "all-in", "shoved", "jammed", "snap", "sizing",
|
||||
)
|
||||
|
||||
|
||||
def _looks_strategic(msg: str) -> bool:
|
||||
low = msg.lower()
|
||||
return len(msg) >= 40 and any(c in low for c in _STRAT_CUES)
|
||||
|
||||
|
||||
def _named_hits(msg: str) -> list[int]:
|
||||
"""Ids of known *named* villains whose name appears as a word in the message."""
|
||||
@@ -113,12 +127,26 @@ def scout(user_msg: str, venue: str | None = None, session_id: int | None = None
|
||||
context=f'Brian referred to "{span}"', session_id=session_id,
|
||||
confidence=res["confidence"])
|
||||
|
||||
if not lines:
|
||||
# Pattern desk: on genuine strategy talk, recall his own past leaks/wins in
|
||||
# similar spots. Gated so routine logging never pays for an embed.
|
||||
pattern: list[str] = []
|
||||
if _looks_strategic(msg):
|
||||
for r in poker.recall_similar_rituals(msg, exclude_session=session_id):
|
||||
tag = "leak" if r["kind"] == "scar" else "banked"
|
||||
cls = f"/{r['classification']}" if r.get("classification") else ""
|
||||
when = f" ({str(r['when'])[:10]})" if r.get("when") else ""
|
||||
pattern.append(f"[{tag}{cls}]{when} {r['content']}")
|
||||
|
||||
if not lines and not pattern:
|
||||
return None
|
||||
logbus.log("info", "scouting desk", hits=len(lines))
|
||||
return ("SCOUTING DESK — structured recall for players in his message (cite it, "
|
||||
"don't invent; if unsure it's the same person, ask him):\n• "
|
||||
+ "\n• ".join(lines))
|
||||
logbus.log("info", "scouting desk", hits=len(lines), patterns=len(pattern))
|
||||
out = "SCOUTING DESK — structured recall for his message (cite it, don't invent; " \
|
||||
"if unsure it's the same person, ask him):"
|
||||
if lines:
|
||||
out += "\n• " + "\n• ".join(lines)
|
||||
if pattern:
|
||||
out += "\nYou've been in a spot like this before —\n• " + "\n• ".join(pattern)
|
||||
return out
|
||||
except Exception as exc: # desk must never break a turn
|
||||
logbus.log("error", "scouting desk failed", error=str(exc)[:160])
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user