# File lib/ruote/exp/flow_expression.rb, line 493
    def do_reply_to_parent(workitem, delete=true)

      # propagate the cancel "flavour" back, so that one can know
      # why a branch got cancelled.

      flavour = if @msg.nil?
        nil
      elsif @msg['action'] == 'cancel'
        @msg['flavour'] || 'cancel'
      elsif h.state.nil?
        nil
      else
        @msg['flavour']
      end

      # deal with the timers and the schedules

      %w[ timeout_schedule_id job_id ].each do |sid|
        @context.storage.delete_schedule(h[sid]) if h[sid]
      end
        #
        # legacy schedule ids, to be removed for ruote 2.4.0

      @context.storage.delete_schedule(h.schedule_id) if h.schedule_id
        #
        # time-driven exps like cron, wait and once now all use h.schedule_id

      h.timers.each do |schedule_id, action|
        @context.storage.delete_schedule(schedule_id)
      end if h.timers

      # cancel flanking expressions if any

      cancel_flanks(h.state == 'dying' ? 'kill' : nil)

      # trigger or vanilla reply

      if h.state == 'failing' # on_error is implicit (#do_fail got called)

        trigger('on_error', workitem)

      elsif h.state == 'cancelling' && h.on_cancel

        trigger('on_cancel', workitem)

      elsif h.state == 'cancelling' && h.on_re_apply

        trigger('on_re_apply', workitem)

      elsif h.state == 'timing_out' && h.on_timeout

        trigger('on_timeout', workitem)

      elsif h.state == nil && h.on_reply

        trigger('on_reply', workitem)

      elsif h.flanking && h.state.nil?
        #
        # do vanish

        do_unpersist

      elsif h.lost && h.state.nil?
        #
        # do not reply, sit here (and wait for cancellation probably)

        do_persist

      elsif h.trigger && workitem['fields']["__#{h.trigger}__"]
        #
        # the "second take"

        trigger(h.trigger, workitem)

      else # vanilla reply

        filter(workitem) if h.state.nil?

        f = h.state.nil? && attribute(:vars_to_f)
        Ruote.set(workitem['fields'], f, h.variables) if f

        workitem['sub_wf_name'] = @h.applied_workitem['sub_wf_name']
        workitem['sub_wf_revision'] = @h.applied_workitem['sub_wf_revision']

        leave_tag(workitem) if h.tagname

        (do_unpersist || return) if delete
          # remove expression from storage

        if h.parent_id && ! h.attached

          @context.storage.put_msg(
            'reply',
            'fei' => h.parent_id,
            'workitem' => workitem.merge!('fei' => h.fei),
            'updated_tree' => h.updated_tree, # nil most of the time
            'flavour' => flavour)

        else

          @context.storage.put_msg(
            (h.forgotten || h.attached) ? 'ceased' : 'terminated',
            'wfid' => h.fei['wfid'],
            'fei' => h.fei,
            'workitem' => workitem,
            'variables' => h.variables,
            'flavour' => flavour)

          if
            h.state.nil? &&
            h.on_terminate == 'regenerate' &&
            ! (h.forgotten || h.attached)
          then
            @context.storage.put_msg(
              'regenerate',
              'wfid' => h.fei['wfid'],
              'tree' => h.original_tree,
              'workitem' => workitem,
              'variables' => h.variables,
              'flavour' => flavour)
              #'stash' =>
          end
        end
      end
    end