private void executeFrom(final int commandIndex, File baseDir) {
    if (commandIndex >= commands.size()) {
      callback.message(null, "Finished OK " + passedStages.size() + " tests", StatusCallback.Type.message);
      actionCallback.complete(null);
      return;
    }

    CommandDescriptor commandDescriptor = commands.get(commandIndex);

    PlaybackCommand command = createCommand(commandDescriptor.fullLine, commandDescriptor.line, commandDescriptor.scriptDir);
    if (isStopRequested || command == null) {
      callback.message(null, "Stopped", StatusCallback.Type.message);
      actionCallback.cancel(false);
      return;
    }

    @SuppressWarnings("unchecked")
    Set<Class<?>> facadeClassesClone = (Set<Class<?>>)facadeClasses.clone();
    PlaybackContext context = new PlaybackContext(this, callback, commandIndex, myRobot, useDirectActionCall, useTypingTargets, command,
                                                  baseDir, facadeClassesClone) {
      private final long myTimeStamp = myContextTimestamp;

      @Override
      public void pushStage(StageInfo info) {
        currentStageDepth.add(info);
      }

      @Override
      public StageInfo popStage() {
        if (!currentStageDepth.isEmpty()) {
          return currentStageDepth.remove(currentStageDepth.size() - 1);
        }

        return null;
      }

      @Override
      public int getCurrentStageDepth() {
        return currentStageDepth.size();
      }

      @Override
      public void addPassed(StageInfo stage) {
        passedStages.add(stage);
      }

      @Override
      public boolean isDisposed() {
        return myTimeStamp != myContextTimestamp || isStopRequested;
      }

      @Override
      public void storeRegistryValue(String key) {
        if (!registryValues.containsKey(key)) {
          registryValues.put(key, Registry.stringValue(key));
        }
      }

      @Override
      public void setProject(@Nullable Project project) {
        myRunner.setProject(project);
      }

      @Override
      public @NotNull Project getProject() {
        Project project = myRunner.getProject();
        if (project == null) {
          throw new IllegalStateException(
            "Project is null. Use a project-aware runner and check if its project has been set up properly");
        }
        return project;
      }
    };

    commandStartStopProcessor.startOfCommand(commandDescriptor.fullLine);

    CompletableFuture<?> commandFuture = command.execute(context);
    Context initialContext = Context.current();
    commandFuture.whenComplete((result, error) -> {
      if (error != null) {
        commandStartStopProcessor.endOfCommand(error.getMessage());
        callback.message(null, "Stopped: " + error, StatusCallback.Type.message);
        LOG.warn("Callback step stopped with error: " + error, error);
        actionCallback.completeExceptionally(error);
        return;
      }

      commandStartStopProcessor.endOfCommand(null);
      try (Scope ignored = initialContext.makeCurrent()) {
        if (command.canGoFurther()) {
          int delay = getDelay(command);
          if (delay > 0) {
            if (SwingUtilities.isEventDispatchThread()) {
              EdtScheduledExecutorService.getInstance().schedule(Context.current().wrap(() -> {
                if (!onStop.isDisposed()) {
                  executeFrom(commandIndex + 1, context.getBaseDir());
                }
              }), delay, TimeUnit.MILLISECONDS);
            }
            else {
              LockSupport.parkUntil(System.currentTimeMillis() + delay);
              executeFrom(commandIndex + 1, context.getBaseDir());
            }
          }
          else {
            executeFrom(commandIndex + 1, context.getBaseDir());
          }
        }
        else {
          callback.message(null, "Stopped: cannot go further", StatusCallback.Type.message);
          actionCallback.complete(null);
        }
      }
    });
  }
private void executeFrom(final int commandIndex, File baseDir)