App-karr

 view release on metacpan or  search on metacpan

lib/App/karr/Cmd/Move.pm  view on Meta::CPAN

      # App::karr::Config/_usage_error prints for a rejected one.
      die 'New status required (valid: ' . join( ', ', @statuses ) . "):\n"
        . command_hint( 'move', $id, 'STATUS' ) . "\n"
        unless $task_new_status;

      # A move to the status the card already has, with no claim to hand over,
      # changes nothing -- so it writes nothing: the write is what stamps
      # `updated` and appends the activity-log entry, and both were saying a
      # move happened when none did (#231). Assigned on every attempt rather
      # than only in the branch where it is true: the callback re-runs on
      # contention, and this answer belongs to the revision that attempt read.
      #
      # --claim is what makes it not this case: `move ID <same status> --claim
      # NAME` writes claimed_by and a fresh claimed_at, which is how an agent
      # takes over a card whose claim ran out without moving it, and dropping
      # that silently would be this ticket's own bug pointing the other way.
      # kanban-md short-circuits in front of its claim handling and does drop
      # it; karr's claims expire and gate `pick`, so here the claim wins.
      #
      # After check_claim, not before it: whether somebody else's live claim
      # blocks this command is a question about the card, not about the work,
      # and kanban-md asks it in the same order.
      $unchanged = $task->status eq $task_new_status
        && !( defined $claim && length $claim );
      return $self->no_change if $unchanged;

      if ( defined $claim && length $claim ) {
        $task->claimed_by($claim);
        $task->claimed_at(gmtime->datetime . 'Z');
      }

      $old_status = $self->apply_status_change($task, $task_new_status, $claim);
    });

    if ($unchanged) {
      # The wording `karr archive` already uses for the same answer, and ADR
      # 0002's exit 0: the card is where it was asked to be, which is success
      # and not a failure to report.
      printf "Task %d is already at %s: %s\n", $task->id, $task->status, $task->title
        unless $self->json;
      # `changed` is the field a --json reader keys on, and it is on both
      # answers rather than only on this one: a key that appears only when
      # nothing happened has to be tested for existence instead of for its
      # value, and tells a reader nothing at all on the karr that never wrote
      # it. old_status/new_status stay, holding the one status the card has, so
      # the shape of a move result does not change with its outcome.
      #
      # Neither report is called here. Nothing was written, so nothing stepped
      # over the expired claim this card may carry, and nothing took up work
      # that its dependencies could still be waiting on -- and
      # apply_status_change did not record either of them for this id.
      return { id => $task->id, title => $task->title, old_status => $task->status,
               new_status => $task->status, changed => JSON::MaybeXS::false() };
    }

    printf "Moved task %d: %s -> %s\n", $task->id, $old_status, $task->status unless $self->json;
    # After the write, not inside the guarded callback that decided it: see
    # App::karr::Role::DependencyCheck/dependency_report. Under --json the pair
    # it returns lands in this hash instead of on STDERR. Same for the expired
    # claim this move may have stepped over
    # (App::karr::Role::ClaimTimeout/expired_claim_report, #177), which is
    # reported first because it is about who held the card, not about the work.
    return { id => $task->id, title => $task->title, old_status => $old_status,
             new_status => $task->status, changed => JSON::MaybeXS::true(),
             $self->expired_claim_report( $task->id ),
             $self->dependency_report( $task->id ) };
  });

  $self->sync_after;

  $self->print_json_results(@$results);

  $self->report_batch_failure($failed, scalar @ids);
}

# The status looked up here is the card's own, not one the caller typed -- only
# --next/--prev come through -- so a miss means the card sits in a column this
# board does not configure and no relative move can be computed from it. The
# card is named, the board's vocabulary is printed, and the way out is the
# explicit form (ticket k263).
sub _status_index {
  my ($self, $statuses, $status, $id) = @_;
  for my $i (0..$#$statuses) {
    return $i if $statuses->[$i] eq $status;
  }
  die "Task $id is at '$status', which this board does not configure (valid: "
    . join( ', ', @$statuses ) . "):\n"
    . command_hint( 'move', $id, 'STATUS' ) . "\n";
}

# App::karr::Role::TaskMutation raises the require_claim message and cannot know
# which command is running; its default names `karr edit ID --status STATUS`.
# Here the status is a positional and the caller has just typed it, so the
# suggestion is the caller's own command line with the one missing piece added.
sub claim_hint_tokens {
  my ( $self, $task, $status ) = @_;
  return ( 'move', $task->id, $status, '--claim', 'NAME' );
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::karr::Cmd::Move - Change a task's status

=head1 VERSION

version 0.601

=head1 SYNOPSIS

    karr move 7 done
    karr move 7 --next
    karr move 7,8,9 in-progress --claim agent-fox



( run in 1.521 second using v1.01-cache-2.11-cpan-85d3896f969 )