Skip to content

fix(storage): drop settled swap requests instead of keeping them forever (#783) - #784

Merged
tempus2016 merged 1 commit into
mainfrom
fix/783-prune-swap-requests
Aug 13, 2026
Merged

fix(storage): drop settled swap requests instead of keeping them forever (#783)#784
tempus2016 merged 1 commit into
mainfrom
fix/783-prune-swap-requests

Conversation

@tempus2016

Copy link
Copy Markdown
Owner

Fixes #783

Problem

async_approve_swap flipped the request's status to "approved" and left the record in storage forever. Nothing ever reads it — both readers filter to pending:

  • websocket.py:367[r for r in get_swap_requests() if r.get("status") == "pending"]
  • coord_chores.py:177 — matches on id and status == "pending"

The panel's only swap UI is the pending-approval queue (taskmate-panel.js:3185); there is no approved-swap history view. fairness_report is built from completions, which after a swap are already recorded against the swapped-to child, so it reflects the outcome without the request record.

The lifecycle asymmetry gave away the original intent: async_reject_swap deleted the record outright, while approve merely annotated it.

Fix

Approval consumes the request, matching rejection. Nothing is lost — the approval remains observable through the taskmate_swap_approved event (carrying chore_id, requester_id, from_child_id, timestamp) and, since #782, through the chore's own dated assignment_swap_child_id / assignment_swap_date override.

Why not a prune job. async_prune_history keeps completions for 90 days because they are real history someone might want. These records are read by nothing, so a retention policy would just mean holding dead data for 90 days instead of forever.

Migration. Existing installs are swept at load, otherwise the fix would only stop new records accumulating. It runs on every load rather than behind a one-shot flag — it is a cheap list filter over a small list, and it also cleans up after a downgrade to a version that still wrote the records. Pending requests are preserved, including legacy records with no status key (dropped, since only an explicit "pending" is actionable).

Verification

Unit — 5 new tests: approval removes the request, double-approve still raises, and three covering the load-time sweep (settled dropped / pending preserved / absent list harmless). The two behavioural ones fail on main. Full suite 1551 passed.

Live instance. Ran three swap-and-approve cycles on unpatched main to accumulate records, then restarted on this branch with debug logging enabled:

DEBUG [custom_components.taskmate.storage] Dropped 8 settled swap request(s) from storage

Eight — the three I had just made plus five genuinely accumulated from earlier testing, which is the bug in miniature. Then a fresh swap-approve-complete cycle passed end to end, and the next restart reported nothing left to drop, confirming new approvals leave no residue.

Noted, not fixed

Removing a chore does not cascade to its pending swap requests, so a deleted chore can leave an orphan in the approval queue. Separate from this change; happy to open an issue if you want it chased.

…ver (#783)

`async_approve_swap` only flipped the request's status to "approved" and
left the record in storage. Nothing reads a request once it leaves
"pending" — `_build_state_snapshot` and `async_approve_swap` both filter
on it, and the panel's only swap UI is the pending-approval queue — so
approved records were write-only data that accumulated indefinitely.

Approval now consumes the request, exactly as rejection already did. The
asymmetry was the giveaway: `async_reject_swap` deleted the record while
approve merely annotated it. Nothing is lost — the approval is still
observable through the `taskmate_swap_approved` event (which carries
chore_id, requester_id, from_child_id and timestamp) and, since #781,
through the chore's own dated `assignment_swap_*` override.

A retention/prune job would have been the wrong shape here.
`async_prune_history` keeps completions for 90 days because they are real
history someone might want; these records are read by nothing, so a
retention policy would only mean holding dead data for 90 days rather
than forever.

Existing installs are swept at load. That runs on every load rather than
behind a one-shot flag: it is a cheap list filter, and it also cleans up
after a downgrade to a version that still wrote the records.

Verified on a live instance — the load-time sweep dropped 8 genuinely
accumulated records from the dev store, and a restart after a fresh
approval found nothing left to drop.
@github-actions github-actions Bot added backend Python integration code tests Test suite labels Aug 13, 2026
@tempus2016 tempus2016 added this to the v5.1.1 milestone Aug 13, 2026
@tempus2016 tempus2016 added the bug Something isn't working label Aug 13, 2026
@tempus2016
tempus2016 merged commit cb13227 into main Aug 13, 2026
16 checks passed
@tempus2016
tempus2016 deleted the fix/783-prune-swap-requests branch August 13, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Python integration code bug Something isn't working tests Test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Approved swap requests are never pruned from storage

1 participant