Skip to main content

🛠️ Creating or Updating a Protocol

This guide walks you through the complete process of creating a new protocol or updating an existing one for problem state management.

1. Define or update the Protocol

Create or update a protocol following the Protocol Definition guidelines.

Location: Create your protocol JSON file in the following GitHub location:
🔗 arc-api/modules/care_management/problems/app/models/problems/protocol_definitions/

Example: See the Eye Health Concerns protocol:
📄 eye_health_concerns.json

Ensure all required data fetchers exist, or create any missing ones.

📋 Protocol Workflow Example: Eye Health Concerns

Initial State: "assess"

When a new Eye Health Concerns problem is created:

  • Default state: assess (marked as initial: true)
  • Automatic intervention creation: Creates a CoordinateEyeCare intervention
  • Assignment: Goes to the ECM Care Manager team (role: "ecm_cm")
  • Due date: 1 week from creation (due_date: "1.week")
  • Deduplication: Prevents duplicate interventions with the same key

Transition Rule: assess → completed

The protocol automatically transitions from "assess" to "completed" when:

"rule": {
"type": "condition",
"parameter": {
"key": "most_recent_completed_intervention",
"args": {
"intervention_types": ["Ecm::Interventions::CoordinateEyeCare"],
"since_last_state_transition": true
}
},
"operator": "eq",
"value": "Ecm::Interventions::CoordinateEyeCare"
}

What the Rule Does

This rule checks if:

  • Most recent completed intervention is of type Ecm::Interventions::CoordinateEyeCare
  • Since last state transition (since_last_state_transition: true) - only looks at interventions completed after the problem entered the current state
  • Exact match (operator: "eq") - the intervention type must exactly equal Ecm::Interventions::CoordinateEyeCare

Fetcher Connection

The most_recent_completed_intervention parameter connects to a fetcher that:

  • Queries the system for completed interventions
  • Filters by the specified intervention types
  • Returns only interventions completed since the last state transition
  • Provides the most recent one for evaluation

Complete Flow

  1. Problem created → Enters "assess" state
  2. Intervention assigned → CM team gets CoordinateEyeCare task (due in 1 week)
  3. CM completes intervention → Marks eye care coordination as complete
  4. Automatic transition → Problem moves to "completed" state
  5. Problem closed → Workflow ends

2. Enable the Problem Type

Before your protocol can be used, you need to enable it in two configuration locations:

A. Add to Protocol Rollout Config

Add the problem type to the desired Care Pod in:

APP_CONFIG.ProblemStates.ProtocolRolloutConfig

Configuration Sources:

  • Local Development: Configure in your local_params.yml file
  • Remote Environments: This configuration gets overridden using AWS Parameter Store (No Flipper feature flag)

Why this is needed:
The ProtocolRolloutConfig is a feature flag system that controls which Care Pods have access to specific medical protocols for different health problems.

How Protocol Rollout Configuration Works

Care Pod Control
The groups array in the config represents different Care Pods that can have customized protocol access.

Problem-Based Protocols
Each health problem in ALLOWED_PROBLEMS can have its own care protocol with states, transitions, and workflows.

Gradual Rollout
Instead of enabling new protocols for all Care Pods at once, you can roll them out gradually.

⚠️ Disclaimer: The configuration example below might be outdated. Please ask a team member to share their current local configuration to ensure you are using the latest settings.

ProblemStates:
ProtocolRolloutConfig: '{
"all": {
"enabledProblems": [
"housing",
"depression",
"type_one_diabetes",
"type_two_diabetes",
"food_insecurity",
"transportation_insecurity",
"inadequate_housing",
"financial_insecurity",
"high_blood_pressure",
"wellness_visit",
"cervical_cancer_screening"
]
},
"groups": [
{
"carepods": [
{
"id": "7b816b54-e2f4-4908-80fa-fcb20c319539",
"name": "Pod C"
},
{
"id": "c0cbb869-ee45-433f-9d2c-1adf0cd4f2ba",
"name": "Pod D"
}
],
"enabledProblems": [
"social_isolation",
"oral_health_concerns",
"preventative_dental_care",
"eye_health_concerns"
]
}
]
}'
  • "all": All Care Pods receive the protocols listed in enabledProblems under "all", regardless of whether the groups array is empty.

B. Add to Allowed Problems List

Add to:

Problems::ProblemProtocol::ALLOWED_PROBLEMS

File Reference: problem_protocol.rb#L8

Why this is needed
  • Business Logic: This validation ensures that only approved problem types can be created as ProblemProtocol records. Any problem value that isn't in the ALLOWED_PROBLEMS list will be rejected.

  • Data Integrity: By maintaining this whitelist, the application prevents invalid or unsupported problem types from being stored in the database.

What happens without this?

If new_protocol is not in the ALLOWED_PROBLEMS array, any attempt to create a ProblemProtocol with problem: 'new_protocol' would fail validation and raise an error in the following step (Step 3: Sync the Protocol to the Database).

3. Sync the Protocol to the Database

Protocol Installation Process

The SyncProblemProtocolsJob serves as the installation mechanism for protocols. When you run the job, it creates a new database record that "installs" the protocol definition into the active system.

Problems::SyncProblemProtocolsJob.perform_now("depression")
How Protocol Installation Works

When ProblemProtocol.update_protocol("eye_health_concerns") is called, it:

  1. Reads the protocol definition from the file system
  2. Creates a new database record with the protocol configuration
  3. Assigns a version number (incremented from previous versions)
  4. Stores the complete definition as structured data

The Installed Protocol Record

The example shows an installed "eye_health_concerns" protocol:

#<Problems::ProblemProtocol:0x000000015abf12c8
id: "168a06dd-5539-488b-8b9d-0b3a781b614a",
problem: "eye_health_concerns",
version: 2, # <- Version tracking for changes
definition: {
# Complete protocol workflow definition
"states" => [...],
"transitions" => [...]
},
definition_hash: "0f8a576c57b0c8359895f249c1f4873c", # <- Content fingerprint
created_at: "2025-08-12 09:06:14.498835000 -0700",
updated_at: "2025-08-12 09:06:14.498835000 -0700"
>

Installation Flow: File Definition → SyncJob → Database Record → Active Protocol

After this, new problem goals of this type will be automatically evaluated and linked to the protocol.

Existing problems won't be linked to the new protocol version automatically (we are linking the problem goal with the specific protocol in problem_goal.state.problem_protocol_id). To link existing problems to the new version of the protocol, run the evaluator job:

protocol = Problems::ProblemProtocol.current_protocol('depression')
Problems::ProblemProtocolEvaluatorJob.perform_now(protocol, skip_interventions:true, dry_run: false)

Note: For local development, it is recommended to use the Rails console to link existing problems to the protocol. For DEV and PROD environments, please use the rake task described in Step 4: Backfill Existing Problems to ensure proper backfilling and auditing.

What this job does
  • Finds all existing problem goals of the specified type
  • Links them to the current protocol version by setting problem_goal.state.problem_protocol_id
  • Evaluates each problem against the protocol rules and transitions them to the appropriate state
  • Creates any required interventions based on the protocol definition

Note: The dry_run: true parameter is primarily useful for production environments to validate changes before applying them. For local development and testing, run with dry_run: false to perform the actual linking. See the deployment and release documentation for production deployment procedures.

5. Completion

At this point:

  • The protocol is stored in your local database
  • Existing problems of that type are linked to the protocol
  • All new problems of the same type will be evaluated and linked on creation

📝 Note: This process updates your local environment only. To deploy protocol changes to higher environments (dev and production), refer to the environment switching documentation for proper deployment procedures.

6. Relative/Optional

Some problems have interventions created when the problem is started. In those cases, we want to block those so that automatic intervention creation only happens through the protocol. In order to do that, we can add an early return in the initial_interventions method in the problem's model class, based on the Guardian. For example, for type_one_diabetes:

def initial_interventions
return [] if Problems::Guardian.allowed_to_continue?(problem)

Flipper.enabled?(:care_gaps_automations) ? [monitor_vitals, coordinate_pair_team_appointment] : [custom]
end

7. Testing

You can now create a problem goal of that type for a patient in an enabled care pod (look at APP_CONFIG.ProblemStates.ProtocolRolloutConfig to see the enabled care pods), either manually or by a trigger (e.g. completing a PHQ9 with depression indications will create a depression problem). The new problem should have a state assigned to it on creation. Every problem goal with a state will display the state dropdown on the careplan, while problem goals with no state will display the state chip/dropdown (depending on current status).

8. Rollout to Dev and Production Environments

In order to release this protocol to lower environments, it's important to perform the following steps:

Dev Environment Rollout

Step 1: Update AWS Parameter Store

  1. Navigate to AWS Systems Manager → Parameter Store
  2. Locate the parameter: /Dev/RailsApi/ProblemStates/ProtocolRolloutConfig
  3. Update the value to include your new protocol in the enabled problems list

Step 2: Force ECS Deployment

After updating the parameter value, force an ECS deployment to apply the configuration changes:

📄 Follow the ECS Deployments documentation for detailed deployment instructions.

Step 3: Sync the Protocol to the Database

Step 4: Backfill Existing Problems

Run the backfill script to update existing problems with the default state of the protocol:

bundle exec ruby script/run_task.rb dev 'rake problem_states:backfill_care_pod[enabled,eye_health_concerns,false]'

Alternative for Step 3: Connect to the bastion instance following the Bastion documentation, run the Rails console, and perform steps 3 and 4 manually.

Production Environment Rollout

📋 Resource: Use the Protocol Release Checklist Template to track your deployment progress across environments.

Step 1: Update Production Configuration

  1. Navigate to AWS Systems Manager → Parameter Store
  2. Locate the parameter: /Prod/RailsApi/ProblemStates/ProtocolRolloutConfig
  3. Update the value to include your new protocol in the enabled problems list
  4. Force ECS deployment in production
  5. Run the backfill script with production parameters

Step 2: Dry Run Validation

Before applying changes to production, run a dry run to ensure the changes are correct:

bundle exec ruby script/run_task.rb prod 'rake problem_states:backfill_care_pod[enabled,eye_health_concerns,true]'

Step 3: Product Operations Review

After the dry run is performed, a new CSV report will be pushed to S3:

  • S3 Location: prod-arc-bucket/problem_protocol/reports/
  • Region: us-west-2
  • File naming: Follow the protocol name pattern
  • Action: Share the report with the Product Manager for review of charts and patients impacted by the update

Step 4: Perform the Update

Set the flag for dry_run to false for the last argument of the script.

Option 1: Run from Your Local Machine

bundle exec ruby script/run_task.rb prod 'rake problem_states:backfill_care_pod[enabled,eye_health_concerns,false]'

Option 2: Execute via Bastion Server

Connect to the bastion prod server for faster update, especially for a (GA) Global Audience update:

CMD="bundle exec rake problem_states:backfill_care_pod[enabled,eye_health_concerns,false]" ./rails_console.sh

Where eye_health_concerns is the protocol name.