Skip to content

fix(storage): deleting a chore or child drops its pending swap requests (#785) - #786

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

fix(storage): deleting a chore or child drops its pending swap requests (#785)#786
tempus2016 merged 1 commit into
mainfrom
fix/785-orphan-swap-requests

Conversation

@tempus2016

Copy link
Copy Markdown
Owner

Fixes #785

Problem

Deleting a chore or a child left its pending swap requests behind.

async_remove_chore cascaded to completions, last-completed, task groups, scheduled changes and children's chore_order. async_remove_child cascaded to completions, reward claims, transactions, pool allocations, career history, quest and challenge progress, and stripped the child from assigned_to. Neither touched swap_requests.

The panel falls back to "?" for a name it cannot resolve (taskmate-panel.js:3187):

{child: this._esc((req && req.name) || "?"), chore: this._esc((chore && chore.name) || "?")}

so the parent was left with a permanent "? wants to swap ?" row in the approval queue that only rejecting could clear.

Fix

Cascade on delete, matching every other cleanup in the codebase.

Both ends matter for the child cascade. requester_id is who the handover goes to; from_child_id is who it comes from and is rendered in the queue. Either being gone makes the request undeliverable, so remove_swap_requests_for_child filters on both.

Child removal also clears a chore's approved swap override when it pointed at the deleted child, so the chore is not left assigned to someone who no longer exists.

Migration. The load-time sweep added in #784 is extended to drop dangling pending requests, so installs that already deleted a chore or child get cleaned up instead of carrying the stuck row forever. A request now survives only when it is pending and every id it names resolves. from_child_id is exempt when blank — that is the normal shape for a request against a chore with no cached assignee yet, not a dead reference, and there is a test pinning it.

Verification

Unit — 7 new tests (5 cascade, 2 sweep). Full suite 1558 passed.

Two existing tests from #784 needed their fixtures updated: they asserted pending requests survive, but used empty children / chores lists, so under the new orphan rule every one of their records is dangling. They now seed the referenced entities, which is what they always meant — they were testing the settled/pending filter, not the orphan filter.

Live instance. Created two orphans the way a user would — delete the chore out from under a pending request, then delete the requesting child:

seed (unpatched main):
  pending swap_requests now visible to the parent: 2
    145875f5 chore=2701e65e requester=0a7b48d0
    9ae04c83 chore=767dc857 requester=9e9a609c

check (this branch):
  pending swap_requests after restart: 0
  PASS — load-time sweep cleared the pre-existing orphans
  PASS — deleting a chore drops its pending swap request immediately
  PASS — deleting a child drops their pending swap request immediately

The two cascade assertions run with no restart involved. A full swap-request-approve-complete cycle still passes afterwards.

Noted, not fixed

assignment_current_child_id is not cleared when the child it names is deleted, so a rotation chore can point at a ghost until the midnight assignment pass, during which the sensor filter (assignment_current_child_id != child.id) matches nobody and the chore is invisible to the whole pool. That predates the swap feature and is a separate defect — happy to open an issue.

…ts (#785)

`async_remove_chore` cascaded to completions, last-completed, task groups,
scheduled changes and children's chore_order; `async_remove_child`
cascaded to completions, reward claims, transactions, pool allocations,
career history, quest and challenge progress. Neither touched
swap_requests, so a pending request outlived whatever it referred to.

The panel falls back to "?" for a name it cannot resolve
(taskmate-panel.js:3187), leaving the parent a permanent "? wants to swap
?" row in the approval queue that only rejecting can clear.

Both ends of a request matter for the child cascade: `requester_id` is
who the handover goes to, and `from_child_id` is who it comes from and is
rendered in the queue. Either being gone makes the request
undeliverable. Child removal also clears a chore's approved swap
override when it pointed at the deleted child, so the chore is not left
assigned to someone who no longer exists.

The load-time sweep from #784 is extended to drop dangling pending
requests too, so installs that already deleted a chore or child get
cleaned up rather than carrying the stuck row forever. A request now
survives only when it is pending *and* every id it names resolves —
`from_child_id` is exempt when blank, which is the normal shape for a
chore with no cached assignee yet.

Verified on a live instance: two orphans created the way a user would
(delete the chore, then delete the requesting child) sat visibly in the
approval queue on unpatched code, were gone after restarting on this
branch, and both delete paths then dropped their request immediately with
no restart involved.
@tempus2016 tempus2016 added this to the v5.1.1 milestone Aug 13, 2026
@tempus2016 tempus2016 added bug Something isn't working backend Python integration code tests Test suite labels Aug 13, 2026
@tempus2016
tempus2016 merged commit ed19698 into main Aug 13, 2026
16 checks passed
@tempus2016
tempus2016 deleted the fix/785-orphan-swap-requests branch August 13, 2026 09:56
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.

Deleting a chore or child leaves orphaned pending swap requests in the approval queue

1 participant