Skip to content

Daemon-owned summarization worker (L2/L3, priority queue) attached to turn capture #54

Open

Description

@diskerror

Re-attach the L2/L3 summarization pipeline (recovered from the removed
chat.cpp Chat::summarize_turn) to turn capture. v1.0 "rebuild/summarize" half.

Agreed architecture

  • Daemon owns summarization — one background summarizer reuses the loaded
    embedder + memory model (avoids reloading the 90 MB ONNX model per turn,
    which a naive per-turn thread/fork would do now that the chat fork model is
    gone).
  • Priority work-queue of summary requests: turns (L2) > sessions (L3) >
    projects (L4)
    . Order fixed for now (not user-configurable), but structured
    so order / option-handling can change.
  • L2 (immediate): each captured turn is summarized in the background.
    • HTTP /turn captures → daemon summarizes directly.
    • MCP capture_turn (separate process) → poke the daemon via the existing
      SIGUSR1 after writing the turn → near-instant L2.
  • L3 (running session summary): fired when a session has been idle ≥
    [housekeeping] summary_pause_minutes (default 20), per session_id, via
    the housekeeping heartbeat (fires at the pause mark, so the rollup is ready
    before the next turn — rather than next-turn-triggered, which would lag).
  • L4 (project): third priority tier; trigger TBD.

Recovered logic (from chat.cpp @ 2b55ee9)

summarize_turn: L2 = one-sentence memory-model summary → store_summary(turn);
L3 = fold L2 into running session summary via a merge prompt that replies
NO_MERGE on a topic shift (→ complete the old running summary, start a new
one), else updates it. Uses store_summary / current_session_summary /
update_summary_text / set_summary_status / chat_memory — all still present.

Done (foundation, commit 294c2ef)

  • store_summary / current_session_summary take session_guid → L3 is per-session.
  • [housekeeping] summary_pause_minutes (default 20).

Remaining

  • Summarizer worker thread + priority queue in the daemon (reuses one
    RaggerMemory + InferenceClient).
  • Enqueue L2 on capture (HTTP direct; MCP via SIGUSR1 poke).
  • Heartbeat: enqueue L3 for sessions idle ≥ summary_pause_minutes; project (L4) tier.
  • Port the NO_MERGE L2/L3 merge logic; concurrency-safe DB access (separate
    connection / WAL).
  • Caveat to document: if the daemon isn't running, MCP-captured turns stay raw
    L1 until it next runs.

🤖 Generated with Claude Code

Activity

  1. diskerror commented on Jun 3, 2026

    @diskerror
    OwnerAuthor

    Future considerations (captured, not scheduled)

    Daemon-less / cron-driven mode. Because every process shares the same DB,
    the housekeeping+summarization pass can run as a one-shot invocation under
    cron/at instead of the resident daemon. To support cleanly: make
    ragger housekeeping (or a --once / ragger summarize verb) do the pass
    directly when no daemon is running
    , rather than only signalling SIGUSR1.
    Tradeoff: the daemon keeps the embedder+model loaded once, while a cron one-shot
    reloads them per run (fine at 20-min cadence, wasteful at 1-min); cron-only also
    means no immediate L2 — turns summarize on the cron tick. Offer both modes.

    L3 every turn (vs on-pause). The original chat.cpp folded each L2 into the
    running session summary every turn, keeping it always-current for the next
    turn's context — at the cost of a merge call per turn. Current design fires L3
    on the pause to save calls. The priority-queue makes this a when-to-enqueue
    toggle (on capture vs on heartbeat), revisitable without restructuring. Deferred.

  2. diskerror commented on Jun 3, 2026

    @diskerror
    OwnerAuthor

    Correction — restore the ORIGINAL chat pipeline behavior (UI removed only)

    Intent clarified: keep chat.cpp's summarization engine verbatim, just drive it
    from turn capture instead of the REPL and drop the user interface. So:

    • L3 folds EVERY turn (not deferred to the pause). Each turn: L2 summary →
      fold into the running session summary via the NO_MERGE merge prompt. Running
      summary stays always-current for the next turn's context. Accepts the
      per-turn merge call cost (memory model is cheap/local; daemon keeps it loaded).
    • Pause = close-out, not create. summary_pause_minutes (default 20)
      repurposes the old quit_summary: after X min idle, mark the running session
      summary complete so the next turn starts a fresh one. Fired by the heartbeat.
    • Port check_orphaned_turns too — crash recovery for captured-but-unsummarized turns.

    Net deltas from a literal port: fork→daemon worker thread; REPL/quit→capture/pause;
    per-session L3 scoping (foundation done). Supersedes the earlier "L3 on pause only"
    note — that was the wrong read of intent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions