docs: def refresh(self, force: bool = False) -> bool:
          """
          Loads the cauldron.json definition file for the project and populates
          the project with the loaded data. Any existing data will be overwritten,
          if the new definition file differs from the previous one.
  
          If the project has already loaded with the most recent version of the
          cauldron.json file, this method will return without making any changes
          to the project.
  
          :param force:
              If true the project will be refreshed even if the project file
              modified timestamp doesn't indicate that it needs to be refreshed.
          :return:
              Whether or not a refresh was needed and carried out
          """
  
          lm = self.last_modified
          is_newer = lm is not None and lm >= os.path.getmtime(self.source_path)
          if not force and is_newer:
              return False
  
          old_definition = self.settings.fetch(None)
          new_definition = definitions.load_project_definition(
              self.source_directory
          )
  
          if not force and old_definition == new_definition:
              return False
  
          self.settings.clear().put(**new_definition)
  
          old_step_definitions = old_definition.get('steps', [])
          new_step_definitions = new_definition.get('steps', [])
  
          if not force and old_step_definitions == new_step_definitions:
              return True
  
          old_steps = self.steps
          self.steps = []
  
          for step_data in new_step_definitions:
              matches = [s for s in old_step_definitions if s == step_data]
              if len(matches) > 0:
                  index = old_step_definitions.index(matches[0])
                  self.steps.append(old_steps[index])
              else:
                  self.add_step(step_data)
  
          self.last_modified = time.time()
          return True