Keep the conversation across a restart (#363) #25

Closed
claude wants to merge 2 commits from overnight/dialogue-persist-pr into overnight/persona-2p-pr
Contributor

One commit. Vikunja #363.

The follow-up session — the thing that lets "нет, в пять" correct the turn before it — lived in a plain map in memory. Restart mavend mid-exchange and the thread was gone with no trace. You asked for "follow-ups from me or from her", and a daemon restart shouldn't end the conversation.

It's now saved. New table, migration #10, appended at the end of the slice — nothing above it moved:

dialogue_sessions (id, data, ts, ttl_ms, expires_ts)

One row per dialogue id, overwritten each turn. This is state, not history — it's a small table pruned by TTL, not a log of everything you ever said. data is the session as JSON; the store never looks inside it.

Expired sessions do not come back. Load deletes everything past its TTL first, then returns the rest, so a dead session is gone from the DB as well as from memory. A stale session resurrecting itself after a restart would be worse than losing it — she'd answer a follow-up to a conversation you don't remember having.

The map is still the fast path. Writes mirror to the DB best-effort: a DB error costs you the restart safety net, not the turn. Passing no store at all keeps the old memory-only behaviour, so nothing that doesn't have a DB changed.

Tests use a real on-disk DB and a second store handle to actually simulate the restart, not a mock: a reloaded session still inherits the earlier turn, an expired one is absent from both memory and the DB, and delete removes the row.

Two things deliberately left in memory:

  • The three yes/no confirm slots ("do you want me to run X?"). These should stay in memory — bringing a half-answered confirmation back to life across a restart is worse than dropping it.
  • The parked clarify question. That one is a real judgement call, not just storage: it carries an attempt count and a 90s TTL, and "re-ask after a restart" vs "give up" needs deciding. Same table would hold it with one more column. Filed separately.

One known trade: internal/dialogue now imports internal/store for the row type, which weakens the point of the interface it defines. Fixing it properly means an adapter in cmd/mavend; not worth the extra lines today, so it's written down here instead of quietly done.

One commit. Vikunja #363. The follow-up session — the thing that lets "нет, в пять" correct the turn before it — lived in a plain map in memory. Restart mavend mid-exchange and the thread was gone with no trace. You asked for "follow-ups from me or from her", and a daemon restart shouldn't end the conversation. **It's now saved.** New table, migration **#10**, appended at the end of the slice — nothing above it moved: ``` dialogue_sessions (id, data, ts, ttl_ms, expires_ts) ``` One row per dialogue id, overwritten each turn. This is **state, not history** — it's a small table pruned by TTL, not a log of everything you ever said. `data` is the session as JSON; the store never looks inside it. **Expired sessions do not come back.** Load deletes everything past its TTL first, then returns the rest, so a dead session is gone from the DB as well as from memory. A stale session resurrecting itself after a restart would be worse than losing it — she'd answer a follow-up to a conversation you don't remember having. **The map is still the fast path.** Writes mirror to the DB best-effort: a DB error costs you the restart safety net, not the turn. Passing no store at all keeps the old memory-only behaviour, so nothing that doesn't have a DB changed. **Tests** use a real on-disk DB and a second store handle to actually simulate the restart, not a mock: a reloaded session still inherits the earlier turn, an expired one is absent from both memory and the DB, and delete removes the row. **Two things deliberately left in memory:** - **The three yes/no confirm slots** ("do you want me to run X?"). These should stay in memory — bringing a half-answered confirmation back to life across a restart is worse than dropping it. - **The parked clarify question.** That one is a real judgement call, not just storage: it carries an attempt count and a 90s TTL, and "re-ask after a restart" vs "give up" needs deciding. Same table would hold it with one more column. Filed separately. **One known trade:** `internal/dialogue` now imports `internal/store` for the row type, which weakens the point of the interface it defines. Fixing it properly means an adapter in `cmd/mavend`; not worth the extra lines today, so it's written down here instead of quietly done.
claude added 2 commits 2026-07-31 11:22:24 +02:00
Vikunja #363. The follow-up session was a plain in-memory map, so any
mavend restart dropped the thread. It now mirrors to a small TTL-pruned
sqlite table and is loaded on startup; expired sessions are deleted on
load, not revived. Clarify's pending question is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
Owner

Superseded by #47, which landed this whole stack on master as one reviewed integration merge. This PR head is an ancestor of master — its commits are in, nothing here is lost. Closing as merged-by-proxy rather than merged, since the merge came in through #47.

Review threads on this PR were answered or acted on before the merge; the Russian wording fixes went in as #48.

Superseded by #47, which landed this whole stack on master as one reviewed integration merge. This PR head is an ancestor of master — its commits are in, nothing here is lost. Closing as merged-by-proxy rather than merged, since the merge came in through #47. Review threads on this PR were answered or acted on before the merge; the Russian wording fixes went in as #48.
kami closed this pull request 2026-07-31 20:22:14 +02:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kami/Maven#25