feat(tasks): persist unsent prompt drafts across restarts - #273
Merged
johannesjo merged 1 commit intoSep 9, 2026
Merged
Conversation
Text typed into a task's "Send a prompt" box lived only in a component signal, so quitting the app or restarting the machine discarded it. Task notes were already persisted; the prompt draft now follows the same path. The draft is mirrored into the task store on every keystroke, saved by the existing autosave (1s debounce, 5s max wait, plus a flush on window close), and restored on mount for both active and collapsed tasks. Sending clears it. Claude-Session: https://claude.ai/code/session_011Mkrn85hgdP6oXb6F57s3j
johannesjo
deleted the
task/make-sure-that-notes-and-stuff-entered-77cfc1
branch
September 9, 2026 18:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Text typed into a task's Send a prompt box lived only in a component-local signal. Quitting the app or restarting the machine discarded it. Task notes were already persisted, so the two boxes behaved inconsistently.
Fix
The prompt draft now follows the same path notes already take. A
promptDraftfield on the task is mirrored from the prompt box on every keystroke:src/store/tasks.tsgains asetTaskPromptDraftsetter. Empty text is stored asundefined, so the saved file does not gain apromptDraft: ""key on every task.src/components/PromptInput.tsxwraps its text setter to write through to the store, and hydrates the textarea from the saved draft at mount. Every existing caller of that setter persists too, including the "send notes to prompt" button and coordinator prefills.src/store/persistence.tsserializes the field and restores it on both the active and collapsed task paths, ignoring a non-string value from a corrupt file.src/store/autosave.tstracks the field, so a draft on its own triggers a save.Saving reuses the existing autosave: a 1s trailing debounce capped at 5s during continuous typing, plus a flush on window close. A graceful quit or reboot loses nothing, and a hard power cut loses at most the last few seconds. That matches how notes already behave.
Tests
Eight new tests cover the round trip: save, restore for active and collapsed tasks, corrupt-value rejection, the autosave snapshot, and four component tests for hydration, write-through, and clearing on send.
Full suite passes at 2410 unit tests and 179 client tests, plus compile, typecheck, lint at zero warnings, and Prettier.
Notes for the reviewer
promptDraftActiveflag is deliberately still not persisted. It only gates coordinator hand-back, and the coordinator auto-fire path reads the live textarea contents rather than that flag, so a restored draft still suppresses auto-fire correctly.taskOrder, so their drafts are not written to disk. Those tasks are themselves ephemeral and never persisted, so this is consistent rather than a gap.initialPromptwill block that prompt's auto-send. The same guard already applied before a restart, so this preserves an already-blocked state rather than creating a new one.https://claude.ai/code/session_011Mkrn85hgdP6oXb6F57s3j