Skip to content

create documentation for new claimqueuemoved event#1139

Open
mbiannaccone wants to merge 2 commits intomainfrom
michela/koala-4752-claimmove
Open

create documentation for new claimqueuemoved event#1139
mbiannaccone wants to merge 2 commits intomainfrom
michela/koala-4752-claimmove

Conversation

@mbiannaccone
Copy link
Copy Markdown
Contributor

@mbiannaccone mbiannaccone requested a review from a team as a code owner April 13, 2026 19:45
@aws-amplify-us-west-2
Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-1139.d298pum72820gn.amplifyapp.com

@mbiannaccone mbiannaccone requested a review from rmagier1 April 13, 2026 20:29
@rmagier1
Copy link
Copy Markdown
Contributor

@mbiannaccone I have actually had a customer ask previously why the CLAIM_UPDATED doesnt always trigger for when a claim moves queues. So I was curious and asked claude to explain the different paths we do for moving claims and then asked if you feature would fix this customers complaint with the new effect. If you are curious on the output:

  All Claim Queue Change Paths — Signal Audit

  Your suspicion is confirmed. There are two batch paths that bypass post_save on the Claim model.

  Paths that DO trigger post_save on Claim

  ┌───────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┬──────────┬──────────────────────────────────────┐
  │                     Path                      │                                File                                │   Line   │                Method                │
  ├───────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼──────────┼──────────────────────────────────────┤
  │ move_claim_to_queue()                         │ quality_and_revenue/actions/claim/move_claim_to_queue.py           │ 37       │ .save(update_fields=(...))           │
  ├───────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼──────────┼──────────────────────────────────────┤
  │ move_claim_to_queue_by_name()                 │ same file                                                          │ 56       │ .save(update_fields=(...))           │
  ├───────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼──────────┼──────────────────────────────────────┤
  │ update_claim_current_queue() (note lifecycle) │ api/serializers/note.py                                            │ 1506     │ .save(update_fields=[...])           │
  ├───────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼──────────┼──────────────────────────────────────┤
  │ automate_claim_queue_movement()               │ quality_and_revenue/actions/claim/automate_claim_queue_movement.py │ 46       │ delegates to move_claim_to_queue()   │
  ├───────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼──────────┼──────────────────────────────────────┤
  │ Mock clearinghouse (dev only)                 │ quality_and_revenue/clearinghouse/mock_claim_md.py                 │ 39,48,61 │ .save()                              │
  ├───────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┼──────────┼──────────────────────────────────────┤
  │ Management commands                           │ various                                                            │ —        │ .save() or via move_claim_to_queue() │
  └───────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┴──────────┴──────────────────────────────────────┘

  These are the single-claim paths — UI single-claim moves, note lifecycle, ERA processing, Candid integration, plugin effects, etc. All create a ClaimStateChangeEvent and call .save().

  Paths that DO NOT trigger post_save on Claim

  B1. move_claims_to_history() — move_claim_to_queue.py:91
  ClaimStateChangeEvent.objects.bulk_create(state_change_events)
  Claim.objects.filter(id__in=claims_to_move).update(current_queue=history)
  Moves zero-balance claims from Adjudicated → History. Uses .update() — no Claim post_save.

  B2. MoveBatchClaimsHelper.move() — helpers/move_batch_claims.py:57
  claims.update(current_queue=queue)
  ClaimStateChangeEvent.objects.bulk_create(state_change_events)
  Batch move to any queue. Uses .update() — no Claim post_save.

  What's lost vs preserved in the batch paths

  The ClaimStateChangeEventManager.bulk_create() override (claim_state_change_event.py:11-23) manually fires post_save for each ClaimStateChangeEvent, so:

  ┌─────────────────────────────────────┬────────────────────────────────────┬────────────────────────────────────┐
  │             Side effect             │        B1 (move to history)        │          B2 (batch move)           │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ ClaimStateChangeEvent created       │ Yes (bulk_create)                  │ Yes (bulk_create)                  │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ CLAIM_QUEUE_MOVED plugin event      │ Yes (via CSCE post_save)           │ Yes (via CSCE post_save)           │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ CLAIM__UPDATED plugin event         │ NO                                 │ NO                                 │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ Snooze clearing                     │ NO (moot — zero balance)           │ Yes (manual call)                  │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ Queue list subscription broadcast   │ NO                                 │ Yes (manual broadcast_on_commit()) │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ Claim detail subscription broadcast │ NO                                 │ NO                                 │
  ├─────────────────────────────────────┼────────────────────────────────────┼────────────────────────────────────┤
  │ Candid integration signal           │ N/A (triggered by CSCE, not Claim) │ N/A                                │
  └─────────────────────────────────────┴────────────────────────────────────┴────────────────────────────────────┘

  Bottom line

  Any plugin listening for CLAIM__UPDATED will miss batch queue moves. The CLAIM_QUEUE_MOVED event (via ClaimStateChangeEvent) does fire in both paths though. B2 manually compensates for some missing side effects (snooze, queue broadcast), but B1 doesn't compensate for any Claim-level signals.

  Is there a specific scenario you're troubleshooting, or were you looking for this general audit?

✻ Crunched for 4m 22s

❯ How is this going to be affected with https://github.com/canvas-medical/canvas/pull/18908/changes

  Called github 2 times (ctrl+o to expand)

⏺ This is a great question. This PR (already merged today) is directly relevant to the audit.

  How PR #18908 Changes Things

  The PR adds a new CLAIM_QUEUE_MOVED plugin event, and it's designed in a way that sidesteps the batch path signal gap.

  The clever part: it fires from ClaimStateChangeEvent, not Claim

  Instead of relying on Claim.post_save (which the batch paths skip), the new event fires from ClaimStateChangeEvent.post_save:

  plugin_io/signals.py changes:
  - Adds @receiver(post_save, sender=ClaimStateChangeEvent) to forward_event
  - When a ClaimStateChangeEvent is created, get_action_types() returns QUEUE_MOVED
  - get_changed_concept_model() maps it back to Claim
  - get_changed_instance() returns instance.claim
  - Event includes queue_entered and queue_exited UUIDs in context

  Why this works for ALL paths

  Every queue change path creates a ClaimStateChangeEvent — even the batch paths:

  ┌────────────────────────────────────────┬────────────────────────────────┬─────────────────────────────────────┬──────────────────────────────┐
  │                  Path                  │ Creates ClaimStateChangeEvent? │        CSCE post_save fires?        │ New CLAIM_QUEUE_MOVED fires? │
  ├────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────┤
  │ A: Single claim (.save())              │ Yes (.create())                │ Yes (native)                        │ Yes                          │
  ├────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────┤
  │ B1: move_claims_to_history (.update()) │ Yes (bulk_create())            │ Yes (custom manager manually sends) │ Yes                          │
  ├────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────┼──────────────────────────────┤
  │ B2: MoveBatchClaimsHelper (.update())  │ Yes (bulk_create())            │ Yes (custom manager manually sends) │ Yes                          │
  └────────────────────────────────────────┴────────────────────────────────┴─────────────────────────────────────┴──────────────────────────────┘

  The custom ClaimStateChangeEventManager.bulk_create() at claim_state_change_event.py:11-23 manually calls post_save.send() for each object — and the new forward_event receiver will catch those signals.

  What's still NOT unified

  CLAIM__UPDATED (the generic claim update event) still won't fire for batch paths B1/B2 — that still requires Claim.post_save. But for plugins that specifically need to react to queue changes, CLAIM_QUEUE_MOVED is the right event and it fires universally.

  One thing to double-check

  The custom bulk_create sends signals with raw=True:
  post_save.send(obj.__class__, instance=obj, created=True, using="default", raw=True)

  The new signal handler doesn't check for raw, and the special-case get_action_types for ClaimStateChangeEvent always returns QUEUE_MOVED regardless. So this should work fine. But it's worth verifying in practice since raw=True was originally added for a canvas_core DiffableModels workaround, and
  the forward_event handler is complex enough that there could be edge cases in the full chain.

  TL;DR

  The PR effectively solves the gap for the queue-move use case. Plugins should listen for CLAIM_QUEUE_MOVED (not CLAIM__UPDATED) to get notified of all queue changes including batch moves.

@mbiannaccone
Copy link
Copy Markdown
Contributor Author

@rmagier1 neat! thanks for sharing!! i didn't even realize that was an issue. we're super light on claim events right now, would be nice to build them out and get them tightened up soon

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