[0/8] Stop a task write from dropping metadata or deleting the file - #281
Open
alex-clickhouse wants to merge 1 commit into
Open
[0/8] Stop a task write from dropping metadata or deleting the file#281alex-clickhouse wants to merge 1 commit into
alex-clickhouse wants to merge 1 commit into
Conversation
Two defects in the same code path, both of which lose user data. ``upsert_task`` replaced every column on every call. A caller that rewrote a row for some other reason -- a file move, an appended note -- had to read the row first and pass ``source``, ``source_url``, ``deadline`` and ``tags`` back, or the write nulled them. Two bugs came from a caller that did not: reindex dropped ``tags`` after v018 added the column, and ``task_done`` nulled all four when it moved a file into done/ (fixed in #238 by adding the four lines back). The nulled ``source_url`` also broke duplicate detection, because ``_find_duplicate_tasks`` matches that column first. A completed task stopped matching the source item that created it, so the next sync of that item made a second task. These four columns are now preserve-on-omit: an omitted column keeps its stored value. ``None`` still clears one, and ``""`` still clears ``tags``. Five call sites stop restoring what they never change -- task_done, task_write, task_update, the PATCH content save, and TaskManager.mark_done -- and reindex stops merging file values against the stored row by hand. The move itself wrote the destination and unlinked the source. Both paths are one path when a task is completed twice, because the first completion stores a ``file_path`` under done/ and the second one reads it back as its source, so the sequence wrote the file and then deleted it. The task kept its row and lost its whole markdown file, including the update history. An agent retrying ``task_done`` after a timeout is enough to reach it. ``move_task_file`` now appends and renames, and a rename onto one path is a no-op. Co-Authored-By: Claude <noreply@anthropic.com>
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.
Base of the task-board stack. Two defects in the task write path, both of which
lose user data. They sit below #272 because both are present on main today and
neither depends on the board work.
The metadata columns
upsert_taskreplaced every column on every call. A caller that rewrote a rowfor some other reason — a file move, an appended note — had to read the row
first and pass
source,source_url,deadlineandtagsback, or the writenulled them.
Two bugs came from a caller that did not:
TaskManager.reindexdroppedtagsafter v018 added the column.task_donenulled all four when it moved a file into done/. fix(tasks): preserve task metadata on completion #238 fixed thatone by adding the four lines back.
The nulled
source_urlalso broke duplicate detection, because_find_duplicate_tasksmatches that column first. A completed task stoppedmatching the source item that created it, so the next sync of that item made a
second task.
Those four columns are now preserve-on-omit: an omitted column keeps its stored
value.
Nonestill clears one, and""still clearstags. Five call sitesstop restoring what they never change, and
reindexstops merging file valuesagainst the stored row by hand.
The second completion
The move wrote the destination and unlinked the source. Both are one path when
a task is completed twice, because the first completion stores a
file_pathunder done/ and the second one reads that path back as its source. The sequence
wrote the file and then deleted it. The task kept its row and lost its whole
markdown file, including the update history it had collected.
Reaching it needs nothing unusual. An agent that retries
task_doneafter atimeout sends the call twice, and with #272 the board can move a card out of
Done and back in.
move_task_filenow appends and renames, and a rename ontoone path is a no-op.
Tests
test_task_upsert_preserve.py— the preserve-on-omit rule, its explicitclears, and the FTS text.
test_task_completion.py— three tests for the second completion. All threefail on the old copy-and-unlink shape.
Full suite: 3030 passed.
🤖 Generated with Claude Code