Skip to content

Fix un-completing tasks and field clears; run unit tests in CI - #101

Merged
mapgie merged 5 commits into
mainfrom
claude/design-aesthetic-alignment-y73s4q
Aug 27, 2026
Merged

Fix un-completing tasks and field clears; run unit tests in CI#101
mapgie merged 5 commits into
mainfrom
claude/design-aesthetic-alignment-y73s4q

Conversation

@mapgie

@mapgie mapgie commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Fixes the "list is empty" error when restoring a task marked done in error, and wires unit tests into the pipeline.

The bug

TaskUpdate's fields all default to null, and the Supabase client serializes with encodeDefaults = false, so any field equal to its default is omitted from the request body. Two consequences:

  • markUndone sent TaskUpdate(completedAt = null), which encoded as an empty PATCH body — it matched no rows and decodeSingle failed with "List is empty". (Marking done worked because the timestamp is non-null.)
  • Clearing a field in the edit task sheet (owner, notes, category, due date, reminder) was silently dropped from the payload, leaving the old value in Supabase.

Chores were unaffected: archiveTag already builds its payload as a map, which is why unarchiving chores worked while un-completing tasks didn't.

The fix

Task PATCH bodies are now built as Map<String, String?> payloads (map entries have no defaults, so nulls are sent as explicit JSON nulls):

  • markUndone sends {"completed_at": null}; markDone sends the timestamp.
  • The edit-sheet save (editTaskPayload) always writes its eight editable columns, null meaning clear, and deliberately never touches completed_at / archived_at / reminded so an edit can't clobber completion state.
  • markReminded is unchanged (non-null value, always encoded).

Recorded as LESSONS.md #33.

Tests + pipeline

  • New "Unit tests" CI job in build.yml runs ./gradlew testDebugUnitTest on every PR (uploads reports on failure). The repo had one JVM test file that CI never executed.
  • TaskPayloadTest pins the fix: every editable column present, cleared fields as explicit nulls, completion state untouched, plus a test documenting the serialization behaviour that caused the bug (TaskUpdate(completedAt = null){}).
  • ChoreModelTest: status thresholds (interval and category chores), cadence-pressure fractions and clamping, countdown text, distant detection.
  • TaskModelTest: due date/period → urgency buckets, garbage-date safety, priority parsing.

Checks

  • Changelog fragment added (changelog/unreleased/task-clear-fix.json, patch bump).
  • python3 a11y_check.py clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YZ2FuLCyKuhmXsPMV5RRes


Generated by Claude Code

claude added 5 commits August 26, 2026 23:41
Restoring a task marked done in error failed with "List is empty":
TaskUpdate's fields all default to null and the Supabase client
serializes with encodeDefaults=false, so markUndone's
TaskUpdate(completedAt = null) encoded as an empty PATCH body that
matched no rows. The same omission silently turned "clear owner/notes/
due date" in the edit sheet into no-ops.

Task PATCH bodies are now built as maps (the pattern archiveTag already
used), where null entries encode as explicit JSON nulls: markUndone
sends {"completed_at": null}, and the edit-sheet save always writes its
eight editable columns while never touching completion/archival/reminded
state. Payload builders are extracted and pinned by TaskPayloadTest,
including a test documenting the serialization behaviour behind the bug.

New unit tests also cover chore status thresholds, cadence pressure,
countdown text, and task urgency/priority parsing. A "Unit tests" job in
build.yml now runs testDebugUnitTest on every PR; previously the one
existing test file was never executed by CI.

Recorded as LESSONS.md #33.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ2FuLCyKuhmXsPMV5RRes
The wrapper script was committed without the executable bit, so the new
Unit tests job failed with "Permission denied" before Gradle started.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ2FuLCyKuhmXsPMV5RRes
The wrapper's eval quoted the optional option variables, so when unset
they expanded to empty-string arguments and java tried to load "" as the
main class ("Could not find or load main class" with a blank name) -- the
Unit tests job failed before Gradle started. Expanding them unquoted
contributes zero words when unset. Verified locally: ./gradlew --version
now boots Gradle 8.13. Recorded as LESSONS.md #34.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ2FuLCyKuhmXsPMV5RRes
The previous commit swept in wrapper-verification run state; the root
.gitignore lacked a .gradle/ entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ2FuLCyKuhmXsPMV5RRes
The .gradle/ append landed on the unterminated .claude line, producing
one broken ".claude.gradle/" pattern; restore both entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ2FuLCyKuhmXsPMV5RRes
@mapgie
mapgie marked this pull request as ready for review August 27, 2026 00:06
@mapgie
mapgie merged commit 6d56366 into main Aug 27, 2026
5 checks passed
@mapgie
mapgie deleted the claude/design-aesthetic-alignment-y73s4q branch August 27, 2026 00:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants