def check_flips(mergedStatuses: pd.DataFrame, noteSubset: c.NoteSubset) -> Tuple[bool, str]:
  """Validate that number of CRH notes remains within an accepted bound.

  Assert fails and scoring exits with error if maximum allowable churn is exceeded.

  Args:
    mergedStatuses: NSH DF with new and old data combined.
    maxCrhChurn: maximum fraction of unlocked notes to gain or lose CRH status.

  Returns:
    None
  """
  if len(mergedStatuses) > c.minNumNotesForProdData:
    # Prune notes to unlocked notes.
    mergedStatuses = mergedStatuses[mergedStatuses[c.timestampMillisOfStatusLockKey].isna()]
    # Prune to note subset
    logger.info(
      f"Checking Flip Rate for note subset: {noteSubset.description} (unlocked only), with max new CRH churn: {noteSubset.maxNewCrhChurnRate}, and max old CRH churn: {noteSubset.maxOldCrhChurnRate}"
    )
    if noteSubset.noteSet is not None:
      mergedStatuses = mergedStatuses[mergedStatuses[c.noteIdKey].isin(noteSubset.noteSet)]

    return _check_flips(
      mergedStatuses,
      noteSubset.maxNewCrhChurnRate,
      noteSubset.maxOldCrhChurnRate,
      description=noteSubset.description,
    )
  return False, ""
Validate that number of CRH notes remains within an accepted bound.

  Assert fails and scoring exits with error if maximum allowable churn is exceeded.

  Args:
    mergedStatuses: NSH DF with new and old data combined.
    maxCrhChurn: maximum fraction of unlocked notes to gain or lose CRH status.

  Returns:
    None
  
sourcecode/scoring/note_status_history.py