Skip to content

fix(scratchpad): persist + restore <state> across resume (#713)#730

Merged
hartsock merged 1 commit into
mainfrom
fix/713-persist-scratchpad-state
Jun 28, 2026
Merged

fix(scratchpad): persist + restore <state> across resume (#713)#730
hartsock merged 1 commit into
mainfrom
fix/713-persist-scratchpad-state

Conversation

@hartsock

Copy link
Copy Markdown
Member

What this PR does

On interrupt + auto-resume, newt restored the transcript but silently dropped the scratchpad <state> store (pure in-memory, never persisted) — so a resumed state_get("current_task") returned "no such key" and the model blind-probed for working memory that was gone (round 0 of the live ornith:35b trace).

Option A (the issue's durable fix): ConversationRecord gains an additive scratchpad: BTreeMap (serde-default), snapshotted on save into a new conversations-table scratchpad column (DEFAULT '{}', back-filled on open — mirrors #717). restore_conversation_into_session re-hydrates the live store (clear + replay; restore is a conversation boundary), and the resume banner announces — restored N <state> key(s) so the model reads its task instead of probing.

Provenance-safe: the scratchpad rides the conversations row, never a turn — only turns feed canonical_encoding_v1, so the content-hash is untouched and existing chains verify byte-for-byte.

Test plan

4 tests: persist→load round-trip + chain-verify still green (proves content-hash untouched), the TUI save path threads <state> to the store, restore re-hydrates the live store (state_get resolves, stale key cleared, banner reports count), and the banner empty/singular/plural cases. just check green — newt-core lib 1007, store 53, newt-tui 389; clippy -D warnings + fmt clean.

Out of scope (follow-ups)

The plan-ledger persistence (companion to #714/#718), the recall-excludes-current-conversation fix (#714), the no-result repeat guard (#718).

Fixes #713

🤖 Generated with Claude Code

On interrupt + auto-resume, newt restored the transcript but silently dropped the
scratchpad <state> store (pure in-memory, never persisted), so a resumed
state_get("current_task") returned "no such key" and the model blind-probed for
working memory that was gone (round 0 of the live ornith trace).

Option A (durable): ConversationRecord gains an additive scratchpad: BTreeMap
(serde-default), snapshotted on save into a new conversations-table `scratchpad`
column (DEFAULT '{}', back-filled on open — mirrors #717). restore_conversation_
into_session re-hydrates the live store (clear + replay; restore is a conversation
boundary), and the resume banner announces "restored N <state> key(s)" so the
model reads its task instead of probing.

Provenance-safe: the scratchpad rides the conversations row, never a turn — only
turns feed canonical_encoding_v1, so the content-hash is untouched and existing
chains verify byte-for-byte (proven by scratchpad_round_trips_and_chain_still_verifies).

Scope: the scratchpad only; the plan ledger is a separate follow-up.

Fixes #713

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hartsock hartsock added the risk:low Low-risk change (scoped, tested, no CI/build/hook changes) label Jun 28, 2026
@hartsock hartsock merged commit f1f9975 into main Jun 28, 2026
16 checks passed
@hartsock hartsock deleted the fix/713-persist-scratchpad-state branch June 28, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:low Low-risk change (scoped, tested, no CI/build/hook changes)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(scratchpad): <state> store is wiped on resume — the current_task black hole

1 participant