From 607d0971c4565bd5cae7775a0b67095e950fc3ce Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 26 Apr 2026 17:28:25 -0400 Subject: [PATCH] feat: drawer witness flag inline-edit (T72.3) Memories grow per-flag witness checkboxes (you / host / guest) that auto-submit on change via HTMX. The new POST route emits a manual_edit event with target_kind=memory_witness and a {flag, value} payload; prior_value mirrors the same shape so an inverse edit restores the flag. The drawer's recent-memories query now selects the three witness columns alongside the existing fields so the template can render checkbox state without a second query per row. --- chat/state/manual_edit.py | 18 +++++++ chat/templates/_drawer.html | 17 ++++++ chat/web/drawer.py | 73 ++++++++++++++++++++++++- tests/test_drawer_edits_extended.py | 82 +++++++++++++++++++++++++++-- 4 files changed, 185 insertions(+), 5 deletions(-) diff --git a/chat/state/manual_edit.py b/chat/state/manual_edit.py index 57e6c3b..3bfff79 100644 --- a/chat/state/manual_edit.py +++ b/chat/state/manual_edit.py @@ -24,6 +24,12 @@ T72.1 (Phase 2.5) adds one list-shaped edit: by string equality so the route handler doesn't have to track fact indices across re-renders. +T72.3 adds a per-flag witness toggle: +- ``memory_witness`` — flip one of ``witness_you`` / ``witness_host`` / + ``witness_guest`` on a memory row. Payload's ``new_value`` is a dict + ``{"flag": "you"|"host"|"guest", "value": 0|1}`` and ``prior_value`` + mirrors the same shape so an inverse edit can restore the flag. + Pin toggles intentionally use the existing ``memory_pin_changed`` event (registered in :mod:`chat.state.memory`) rather than ``manual_edit`` so the projection writes both ``pinned`` and ``auto_pinned`` atomically. @@ -37,6 +43,8 @@ from sqlite3 import Connection from chat.eventlog.log import Event from chat.eventlog.projector import on +_VALID_WITNESS_FLAGS = {"you", "host", "guest"} + def _clamp(value: int, lo: int, hi: int) -> int: return max(lo, min(hi, value)) @@ -120,5 +128,15 @@ def _apply_manual_edit(conn: Connection, e: Event) -> None: target_id["target_id"], ), ) + elif kind == "memory_witness": + # T72.3: toggle one of the three witness flags on a memory row. + # ``new_value`` is the dict ``{"flag", "value"}``; ``prior_value`` + # mirrors the same shape so an inverse edit restores the flag. + flag = new_value["flag"] + if flag in _VALID_WITNESS_FLAGS: + conn.execute( + f"UPDATE memories SET witness_{flag} = ? WHERE id = ?", + (1 if int(new_value["value"]) else 0, int(target_id)), + ) # Unknown target_kind: silently no-op for v1. Future kinds (activity # fields, etc.) extend the dispatch above. diff --git a/chat/templates/_drawer.html b/chat/templates/_drawer.html index 228a440..2cf48a7 100644 --- a/chat/templates/_drawer.html +++ b/chat/templates/_drawer.html @@ -347,6 +347,23 @@ +
+ {% for flag in ['you', 'host', 'guest'] %} + {% set witnessed = m['witness_' ~ flag] %} +
+ + + + +
+ {% endfor %} +
Edit POV summary