Summary
Editing a task through the TUI (e on a selected task) and saving resets several
persisted columns to their zero value, even when the user only changed the title or body.
The edit form models a subset of fields; everything it doesn't model — and that the
save-reconciliation doesn't explicitly re-apply — is overwritten with empty/zero on
UpdateTask.
Affected fields
On every TUI edit-save, these are silently reset:
claude_session_id → "" — breaks conversation resume for the task
daemon_session → "" — loses the tmux daemon-session association
port → 0
pr_info_json → "" — cached PR/CI state dropped (PR URL/number survive)
permission_mode → "" and dangerous_mode → false — silently reverts the task
to prompt mode, even if it was set to auto/dangerous
pinned → false — silently unpins a pinned task
tags → "" — tags lost
source_branch → ""
Fields the form does preserve (so they're safe): title, body, status, type, project,
executor, effort level, PR URL/number, worktree path, branch name, timestamps.
Root cause
internal/ui/form.go::NewEditFormModel reads only Title, Body, Type, Project, Executor,
EffortLevel, PRURL, PRNumber from the task. On save,
internal/ui/app.go::updateEditTaskForm calls FormModel.GetDBTask() (which rebuilds a
fresh Task from just those form fields) and then re-applies only a hand-picked subset
(ID, Status, WorktreePath, BranchName, CreatedAt, StartedAt, CompletedAt). It then calls
db.UpdateTask, whose UPDATE tasks SET ... writes a fixed column set — so any column it
writes that was neither in the form nor re-applied gets the zero value.
The same shape affects the project-move paths
(internal/ui/app.go::moveTaskToProject, cmd/task/main.go::moveTask), which rebuild the
task and only copy a subset of fields.
Reproduction
- Create a task; pin it (
p) and set it to auto/dangerous permission mode.
- Let it run so it has a
claude_session_id.
- Select it, press
e, change only the title, save.
- Observe: the task is unpinned, permission mode is back to prompt, and resuming the
conversation no longer finds the prior session.
Suggested fix
Two reasonable directions:
- Targeted: in
updateEditTaskForm's reconciliation block, carry through every
persisted field the edit form doesn't expose (mirror how Status/WorktreePath are
preserved) — i.e. start from the original task and overlay only the form-edited fields,
rather than starting from a blank GetDBTask() result.
- Structural (preferred): have the edit form load and round-trip the full task, or
give UpdateTask a field-mask / partial-update variant so callers only write the
columns they actually intend to change.
Summary
Editing a task through the TUI (
eon a selected task) and saving resets severalpersisted columns to their zero value, even when the user only changed the title or body.
The edit form models a subset of fields; everything it doesn't model — and that the
save-reconciliation doesn't explicitly re-apply — is overwritten with empty/zero on
UpdateTask.Affected fields
On every TUI edit-save, these are silently reset:
claude_session_id→""— breaks conversation resume for the taskdaemon_session→""— loses the tmux daemon-session associationport→0pr_info_json→""— cached PR/CI state dropped (PR URL/number survive)permission_mode→""anddangerous_mode→false— silently reverts the taskto prompt mode, even if it was set to auto/dangerous
pinned→false— silently unpins a pinned tasktags→""— tags lostsource_branch→""Fields the form does preserve (so they're safe): title, body, status, type, project,
executor, effort level, PR URL/number, worktree path, branch name, timestamps.
Root cause
internal/ui/form.go::NewEditFormModelreads only Title, Body, Type, Project, Executor,EffortLevel, PRURL, PRNumber from the task. On save,
internal/ui/app.go::updateEditTaskFormcallsFormModel.GetDBTask()(which rebuilds afresh
Taskfrom just those form fields) and then re-applies only a hand-picked subset(ID, Status, WorktreePath, BranchName, CreatedAt, StartedAt, CompletedAt). It then calls
db.UpdateTask, whoseUPDATE tasks SET ...writes a fixed column set — so any column itwrites that was neither in the form nor re-applied gets the zero value.
The same shape affects the project-move paths
(
internal/ui/app.go::moveTaskToProject,cmd/task/main.go::moveTask), which rebuild thetask and only copy a subset of fields.
Reproduction
p) and set it to auto/dangerous permission mode.claude_session_id.e, change only the title, save.conversation no longer finds the prior session.
Suggested fix
Two reasonable directions:
updateEditTaskForm's reconciliation block, carry through everypersisted field the edit form doesn't expose (mirror how Status/WorktreePath are
preserved) — i.e. start from the original task and overlay only the form-edited fields,
rather than starting from a blank
GetDBTask()result.give
UpdateTaska field-mask / partial-update variant so callers only write thecolumns they actually intend to change.