-
Notifications
You must be signed in to change notification settings - Fork 17
Automatic Case Distribution
When a Veterans Law Judge requests cases that are ready for a decision, those cases are drawn from the Board's various dockets according to a set of rules. Prior to the implementation of the Appeals Modernization Act in February 2019, there was only a single docket, and the distribution of cases could be done by hand. With AMA in place, there are now four dockets, and the rules for distribution have reached a level of complexity that this task now must be automated.
This pages describes the rules by which cases are distributed.
A docket is a line. We can generally think of a docket like a first-in, first-out (FIFO) queue, however there are some exceptions to this rule. One is that a case must be "ready" to be distributed, i.e. it must have no other tasks to be completed before a decision is written. So a ready case will be distributed before a non-ready case, even if the non-ready case is ahead of it on the docket. The Board is also allowed a small amount of wiggle room so that it doesn't have to strictly observe docket order at the cost of efficiency; before AMA this was known as the "docket range," a number of cases that were close enough to the front of the line to be considered eligible to be distributed.
If an appellant is suffering a serious illness, in financial distress, or for another sufficient cause, their appeal can be Advanced on the Docket, or prioritized. If the appellant is older than 75, their appeal is automatically AOD; otherwise, they must file a motion requesting this status.
Board decisions can be appealed to the Court of Appeals for Veterans Claims. If CAVC disagrees with the Board's decision, they will remand it back to the Board for another decision. These post-CAVC remands are also prioritized.
There is no differentiation between the various reasons for prioritizing cases. All priority cases are prioritized equally.
Before AMA, if a judge held a hearing with the appellant, that same judge would be required to write the decision on the appeal. We would say that this appeal is "tied to the judge." A Veteran could waive this right, enabling their appeal to go to any judge. They would be asked to do this if the judge retired, for example. At this point, their case would be deemed "genpop" — eligible to go to any judge.
Under AMA, this is no longer a requirement under law. The link between the VLJ that originally held the hearing has been removed so that cases can be distributed as soon as possible.
The only link that still exists is for legacy hearing cases, as required by law.
In the app, appeals that meet at least one of these conditions are considered genpop:
- All AMA appeals
- All legacy appeals without a hearing
- Legacy appeal where a hearing is not tied to an active judge
When a judge requests a distribution of cases, they will receive a certain number of cases in their queue. This number is called the batch size. It is a multiple of the number of attorneys on the judge's team (currently 3 x the number of attorneys). If the judge does not have their own team, for example if they are a DVC, they receive a set number of cases known as an alternative batch size (currently 15 cases).
We also can calculate a number called a total batch size, which is the sum of all of the individual judge's batch sizes. The total batch size is used as a denominator for calculating things like the optimal percentage of priority cases to distribute.
The legacy docket combines hearing and non-hearing appeals. As a result, it is not optimal to always grab the frontmost case; there may be cases farther back on the docket that can only be distributed to this specific judge, and it will be advantageous to overall timeliness to distribute them ahead of genpop cases that could be worked by anybody. But we still need to respect docket order, and we do this, similar to how the legacy docket was manually managed, by calculating a docket range. This is the range of cases on the legacy docket that are close enough to the front of the line to be distributed.
The legacy docket range is equal to the total batch size, minus the count of priority cases, times the legacy docket proportion (see Docket Proportions below). A hearing case within that range is considered eligible to be distributed to its judge as if it was at the front of the line.
Cases considered "ready" for distribution must have all pre-distribution tasks completed.
For AMA Appeals, this is simply determined by the state of the appeal's distribution task. If this task has a status of "assigned", all child tasks that must be completed for distribution are either complete or cancelled, meaning the appeal is ready for distribution.
Here is an example of an AMA appeal's task tree that is ready for distribution:
For legacy appeals, the appeal must meet the following conditions to be considered ready to distribute:
- The appeal must be active at the board (
bfmpro
of the vacols case is "ACT") - The appeal must be in case storage (
bfcurloc
of the vacols case is "81" or "83") - The appeal must not belong to a special team at the board (
bfbox
of the vacols case is null) - The appeal must not have any open blocking vacols diaries (
tskdcls
is null (diary is open) andtskactcd
is one of 'EXT', 'HCL', or 'POA' of any associated vacols diary record (diary is a blocking diary type)) - The appeal must not have any open blocking vacols mail (
mlcompdate
is null (mail task is open) andmltype
is NOT one of '02', '05', '08', or '13' of any associated vacols mail record (mail task is a blocking distribution type))
appeal = LegacyAppeal.find_by(vacols_id: 3856200)
VACOLS::CaseDocket.priority_ready_appeal_vacols_ids.include? appeal.vacols_id
=> false
# Appeal is considered either not ready or not priority
appeal.aod?
=> true
# Is priority
appeal.location_code
=> "81"
# Appeal is in case storage, one of the conditions for "ready to distribute"
VACOLS::Case.find_by(bfkey: appeal.vacols_id).bfmpro
=> "ACT"
# Appeal is active, one of the conditions for "ready to distribute"
VACOLS::Case.find_by(bfkey: appeal.vacols_id).bfbox
=> nil
# Case does not belong to a special team, one of the conditions for "ready to distribute"
VACOLS::Note.where(tsktknm: appeal.vacols_id, tskdcls: nil, tskactcd: ['EXT', 'HCL', 'POA']).count
=> 0
# No blocking diary items in vacols, one of the conditions for "ready to distribute"
VACOLS::Mail.where(mlfolder: appeal.vacols_id, mlcompdate: nil).pluck(:mltype)
=> ["07"]
# There is an open blocking mail item in vacols, meaning this case is not ready to distribute
Some Appeals have an "affinity window," meaning that Appeal will only be distributed to a specific judge for a certain amount of time. The affinities are:
- CAVC Remands: should be distributed to the judge who wrote the original decision
- AMA Hearing Docket Appeals: should be distributed to the judge who held the hearing for that appeal
- AOJ Remand Returns (legacy only): should be distributed to the judge who wrote the original decision
Note: Legacy appeals are considered tied to a judge if any hearings were held and the judge who held the most recent hearing is still with the board. In this scenario, affinities do not apply and appeals should only distribute to the tied judge (who held the most recent hearing). The only exception to this is if the decision for a remanded case (CAVC or AOJ) was signed by a judge who did not hold the original hearing(s), and the case has not had any additional hearings held since the last decision was written. In that scenario, the case is considered to have affinity to the Judge who signed the original decision.
To determine the judges that have affinity on AMA appeals, we will:
- For CAVC Remands, join the original Appeal via the
cavc_remands
table, then joining the original Appeal'sJudgeDecisionReviewTask
and get theassigned_to
value from that task - For AMA Hearing Docket Appeals, join the most recent hearing, then use the
judge_id
field (corresponding to the User table ID value) for a hearing with a disposition of held
After determining the judge, we need to check to see if the affinity window has elapsed. Prior to the release of APPEALS-44793 Case Distribution Affinity Calculations in FY24Q3, we used the Appeal's distribution_task.assigned_at
value. APPEALS-44793 implemented a new model called AppealAffinity
:
- The value in
appeal_affinity.affinity_start_date
will be compared to the current time at the time of a distribution to determine if the Appeal has been waiting to distribute to its judge with affinity for more than the lever values for the relevant scenario. If an Appeal meets the requirements to be held as an affinity appeal, it must have anappeal_affinity.affinity_start_date
, otherwise it will not be distributed
To populate the AppealAffinity values, we triggers an asynchronous job called UpdateAppealAffinityDatesJob after distributions complete which will:
- Get the DistributedCase records for Appeals which were distributed
- Join the Appeals to get their
receipt_date
values - Group them by
[docket, priority]
on thedistributed_cases
records - Select the most recent
receipt_date
value for each grouping - Using those
receipt_date
values, retrieve all Appeals in the database which are of the[docket, priority]
combination that have the same or olderreceipt_date
- For retrieved Appeals, create a new AppealAffinity record with the current time as the
affinity_start_date
This is done to simulate a "rolling docket number". We don't want affinities determined by when the appeal was made ready to distribute, but instead by when the appeal is actually going to be selected for distribution. This is because non-priority Appeals may be ready to distribute for months before they reach the front of the line, in which case the affinity hold would already have elapsed if we used the distribution task.
For CAVC Remands which are on the Hearing docket, a new hearing is not always required, so a HearingTask subtree is not created by default. For these Appeals, they will use the cavc_affinity_days
rules instead of the ama_hearing_case_affinity_days
rules
To determine the judges that have affinity on legacy appeals, we:
- Join the remand's prior decision using the
JOIN_PREVIOUS_APPEALS
constant in thecase_docket
model - Include that decision's BFMEMID (previous deciding judge) and BFAC (previous type action) in distribution queries
With the AppealAffinity
model, we cannot run queries on the VACOLS database which include direct references to the Caseflow database, so we use the case_docket
queries to select a broad range of ready appeals, and then pass them through filter methods. Those filter methods will reject any appeals which are within the affinity window for a judge who is not currently being distributed to. This slightly reduces performance of the legacy portion of a distribution, but the data set in VACOLS is small enough that it does not have a large impact on the overall distribution process.
The below image details the affinity logic for CAVC remands:
The affinity day values are controlled via the Case Distribution Admin UI.
The Appeals Modernization Act created two new dockets, in addition to the existing "legacy" docket, and allowed VA to create as many additional dockets as it wants. VA decided to make one additional docket, bringing the total number to four. In 2024, the Board requested that we include a way to control case distribution affinities for a subset of legacy appeals known as "AOJ Remand Returns", which led to the creation of a fifth docket class in Caseflow called the "AojLegacyDocket". This is not a docket from the business standpoint, but was added to the code to implement separate affinity day lever values for those cases.
- Legacy docket. The original flavor docket. This docket contains appeals of decisions before AMA took effect. As there was previously only one docket, the legacy docket contains both hearing and non-hearing appeals. It also has an open record, meaning that evidence can be added to an appeal at any time.
- Direct Review docket. This AMA docket contains appeals where the Veteran has decided they do not want to add new evidence and do not want a hearing. To encourage people to use this option, the Board has promised that the average number of days to complete appeals on this docket will be 365 days, one year.
- Evidence Submission docket. On this AMA docket, a Veteran can add evidence during the 90 days after they begin their appeal. There is no timeliness goal for this docket.
- Hearing Request docket. On this AMA docket, the Veteran has requested a hearing with a judge. They can also submit evidence at their hearing, or during the 90 days after the hearing.
-
AOJ Legacy docket. This docket contains a subset of legacy appeals that are known as "AOJ Remand Returns", and are identified in the code and database with a type action of "Post Remand" (
BRIEFF.BFAC == '3'
)
TL;DR
- Runs at 1am EST on Mondays
- Distributes priority cases to Judge Teams who have allow priority push enabled
- Triggers other asynchronous jobs to run when completed:
- UpdateAppealAffinityDatesJob (described in the Affinities section above)
- If it fails
- Confirm that Request More Cases is working
- This is rarely rerun on failure in prod and requires Board Approval to rerun in prod
What it does (high level):
- Retrieves all judges whose JudgeTeam has
accepts_priority_pushed_cases = true
- Retrieves all priority distributions in the past 30 days
- Calculates a "priority target" by adding the number of ready genpop priority Appeals to the number of priority push cases that all judges have received in the past 30 days and dividing that by how many priority push distributions have occurred in the past 30 days
- This is attempting to "level out" the number of cases being distributed to the judges, so that over the course of a month the judges should all receive approximately the same number of priority push cases
- Uses this priority target to determine the number of cases to be distributed to each judge
- If the number cases to be distributed doesn't match the number of ready genpop Appeals, the "leftover" cases will be added to the number of cases to be distributed to the judges who were calculated to receive the fewest cases in the current run of the job
- Creates and executes a new Distribution for each judge, passing in the number of cases to be distributed and
push_priority: true
After the job completes, it pulls a statistics about the job and sends them to #appeals-job-alerts in the "Office of CTO @ VA" Slack workspace
- Allows Judges to manually request more cases
- Distributes Priority and non-priority cases
The Specialty Case Team is an organization that handles special issues on Appeals. These appeals are currently distributed alongside other appeals without affecting the total docket count that a judge would receive. It is available to all 3 AMA docket types, but not a legacy docket and is currently only implemented for AMA appeals. The check for which appeals qualify as SCT appeals is based on the request issue benefit types defined in this constant json file client/constants/SPECIALTY_CASE_TEAM_BENEFIT_TYPES.json
.
The task tree is similar to a typical distributed appeal except the top level task will be a SpecialtyCaseTeamAssignTask instead of a JudgeAssignTask. Example tree below:
February 24, 2021May 2020 Case Distribution Overview(deprecated)Automatic Case Distribution - Board presentationNovember 2018 Presentation with cute robots describing how Automatic Case Distribution works (deprecated)
- Home
- Acronyms and Glossary
- Caseflow products
- Caseflow Intake
- Caseflow Queue
- Caseflow Reader
- Caseflow eFolder
- Caseflow Hearings
- Caseflow Certification
- Caseflow APIs
- Appeal Status API
- Caseflow Dispatch
-
CSUM Roles
- System Admin
- VHA Team Management
- Active Record Queries Resource
- External Integrations
- Caseflow Demo
- Caseflow ProdTest
- Background
- Stuck Jobs
- VA Notify
- Caseflow-Team
- Frontend Best Practices
- Accessibility
- How-To
- Debugging Tips
- Adding a Feature Flag with FeatureToggle
- Editing AMA issues
- Editing a decision review
- Fixing task trees
- Investigating and diagnosing issues
- Data and Metric Request Workflow
- Exporting and Importing Appeals
- Explain page for Appeals
- Record associations and Foreign Keys
- Upgrading Ruby
- Stuck Appeals
- Testing Action Mailer Messages Locally
- Re-running Seed Files
- Rake Generator for Legacy Appeals
- Manually running Scheduled Jobs
- System Admin UI
- Caseflow Makefile
- Upgrading Postgresql from v11.7 to v14.8 Locally
- VACOLS VM Trigger Fix M1
- Using SlackService to Send a Job Alert
- Technical Talks