App-karr

 view release on metacpan or  search on metacpan

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

    ->handoff_status;

  # Handoff used to read the task, mutate it and save it back unguarded, so a
  # claim landing in that window was overwritten rather than obeyed -- the same
  # read-then-write #44/#46/#56 closed everywhere else. update_task_guarded is
  # that closure: the claim rule below is applied to the revision this writes,
  # and re-applied if another agent gets in first (ticket #97).
  my $task = $self->update_task_guarded($id, sub {
    my ($task) = @_;

    # The one claim-ownership rule, shared with move/edit/delete/archive rather
    # than reimplemented here.
    $self->check_claim($task, $self->claim);

    # And the one status-change path, so the handoff obeys the same
    # require_claim, status validation and lifecycle stamps as `karr move`
    # (tickets #54, #55, #68). --claim is required on this command, so a target
    # status flagged require_claim is always satisfied.
    $self->apply_status_change($task, $target, $self->claim);

    # Refresh claim
    $task->claimed_by($self->claim);
    $task->claimed_at(gmtime->datetime . 'Z');

    # Block if requested. length, not truth: --block 0 is a reason (ticket #153,
    # extending ticket #78's rule to the handoff path).
    if (defined $self->block && length $self->block) {
      $task->block($self->block);
    }

    # Append note. length, not truth: --note 0 is a note (ticket #153, the
    # same fix as --append_body in edit).
    if (defined $self->note && length $self->note) {
      my $note_text = $self->note;
      if ($self->timestamp) {
        $note_text = gmtime->strftime('%Y-%m-%d %H:%M') . ' ' . $note_text;
      }
      my $have = defined $task->body && length $task->body;
      $task->body(($have ? $task->body . "\n" : '') . $note_text);
    }

    # Release claim if requested
    if ($self->release) {
      $task->clear_claimed_by;
      $task->clear_claimed_at;
    }
  });

  $self->sync_after;

  # The handoff target is a working column, so apply_status_change above will
  # have recorded any unsatisfied dependencies; this emits them (ticket #123).
  my %dependency = $self->dependency_report( $task->id );

  if ($self->json) {
    $self->print_json({ %{ $task->to_json_hash }, %dependency });
    return;
  }

  my $msg = sprintf "Handed off task %d -> %s", $task->id, $target;
  $msg .= sprintf " (blocked: %s)", $self->block if $self->block;
  $msg .= " (claim released)" if $self->release;
  print "$msg\n";
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::karr::Cmd::Handoff - Hand off a task for review

=head1 VERSION

version 0.500

=head1 SYNOPSIS

    karr handoff 7 --claim agent-fox
    karr handoff 7 --claim agent-fox --note "Implementation complete" --timestamp
    karr handoff 7 --claim agent-fox --block "waiting for QA" --release

=head1 DESCRIPTION

Moves a task into the board's review column -- C<review> where the board
configures one, the last non-terminal column where it does not
(L<App::karr::Config/handoff_status>) -- and refreshes its claim so the next
stage of work can see who handed it off. The command can append a note, add a
blocker, and optionally release the claim after the handoff.

=head1 OPTIONS

=over 4

=item * C<--claim>

Required. Identifies the agent performing the handoff and is validated against
the current claim unless that claim has expired.

=item * C<--note>, C<--timestamp>

Append handoff text to the task body, optionally prefixed with the current UTC
timestamp.

=item * C<--block>, C<--release>

Record a blocking reason and/or clear the claim immediately after the handoff.

=back

=head1 SEE ALSO

L<karr>, L<App::karr>, L<App::karr::Cmd::Pick>, L<App::karr::Cmd::Move>,
L<App::karr::Cmd::Edit>, L<App::karr::Cmd::Log>

=head1 SUPPORT



( run in 0.642 second using v1.01-cache-2.11-cpan-788537b7465 )