feat: view past sessions, edit session details, log rituals while reviewing

- View any past session as a read-only HUD: /session?id=N (hud(session_id) +
  /session/data?id=); /history rows now link there. Closed sessions show played
  duration + final net; recap link when one exists.
- Edit session details during or after play: poker.update_session (recomputes net
  when buy-in/cash-out change), PATCH /session/{id}, an update_session tool ("venue
  was actually Bellagio", "I bought in for 600"), and an inline ✎ Edit form on the HUD.
- Rituals attach to the most-recent session post-close (poker.review_session_id),
  so scar/confidence/reset work while reviewing after you rack up.
- Edit form is poll-safe (won't clobber mid-edit); past-session view doesn't poll.
- test_modes.py +3 (edit, review rituals, past-session HUD); 49 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 05:12:13 +00:00
parent cca8322ee2
commit 559faaed30
7 changed files with 224 additions and 29 deletions
+48 -1
View File
@@ -341,6 +341,50 @@ def _resolve(session_id: int | None) -> int | None:
return live["id"] if live else None
def review_session_id() -> int | None:
"""The session to attach reflective entries to: the live one if any, else the
most-recent real session (closed). Lets rituals/notes land while reviewing after
you've racked up. Excludes the standing 'Hand Reviews' bucket."""
live = live_session()
if live:
return live["id"]
r = _c().execute(
"SELECT id FROM poker_sessions WHERE status != 'review' ORDER BY id DESC LIMIT 1"
).fetchone()
return int(r["id"]) if r else None
_EDITABLE = ("venue", "stakes", "game", "format", "buy_in_total", "cash_out",
"mantra", "mood")
def update_session(session_id: int, **fields) -> dict | None:
"""Edit session details (during or after play). Only known columns are touched;
net is recomputed when buy-in/cash-out change and both are known."""
s = get_session(session_id)
if not s:
return None
sets, vals = [], []
for k, v in fields.items():
if k in _EDITABLE and v is not None:
sets.append(f"{k} = ?")
vals.append(float(v) if k in ("buy_in_total", "cash_out") else v)
if sets:
conn = _c()
with conn:
conn.execute(f"UPDATE poker_sessions SET {', '.join(sets)} WHERE id = ?",
(*vals, session_id))
s = get_session(session_id)
# keep net consistent if the money fields changed and both are present
if s.get("cash_out") is not None and s.get("buy_in_total") is not None:
net = float(s["cash_out"]) - float(s["buy_in_total"])
if net != s.get("net"):
with conn:
conn.execute("UPDATE poker_sessions SET net = ? WHERE id = ?", (net, session_id))
s = get_session(session_id)
return s
def add_buyin(amount: float, session_id: int | None = None) -> float:
"""Add a buy-in/rebuy to a session. Returns the new total in."""
sid = _resolve(session_id)
@@ -1090,7 +1134,10 @@ def hud(session_id: int | None = None) -> dict | None:
"id": sid, "venue": s.get("venue"), "stakes": s.get("stakes"),
"game": s.get("game"), "format": s.get("format"),
"status": s.get("status"), "started_at": s.get("started_at"),
"buy_in_total": s.get("buy_in_total"),
"ended_at": s.get("ended_at"), "hours": s.get("hours"),
"buy_in_total": s.get("buy_in_total"), "cash_out": s.get("cash_out"),
"net": s.get("net"), "mantra": s.get("mantra"), "mood": s.get("mood"),
"is_live": s.get("status") == "live", "has_recap": bool(s.get("recap_md")),
},
"stack": {
"current": state["current"], "buy_in": state["buy_in"], "net": state["net"],