API-Docker

 view release on metacpan or  search on metacpan

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN

```

### List tasks

```bash
karr list                                    # the open cards
karr list --status todo,in-progress          # filter by status
karr list --priority high,critical           # filter by priority
karr list --tag backend                      # filter by tag
karr list --class expedite                   # filter by class of service
karr list --blocked                          # only the blocked cards
karr list --not-blocked                      # only the unblocked ones
karr list --archived                         # the archive, and nothing else
karr list -s "search term"                   # search title/body/tags
karr list --sort priority --reverse          # sort and reverse
karr list --sort priority -n 5 --json        # the five most urgent open cards
karr list --claimed-by agent-1               # filter by claim owner
karr list --unclaimed                        # only what no live claim holds
karr list --compact                          # one-line output (agent-friendly)
karr list --json                             # JSON output
```

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN

arbitrary ones put in order -- that is the "what next" call, instead of pulling
the whole board and cutting it locally.

`--unclaimed` is "what is free right now" -- `claimed_by` unset or empty, or a
claim older than the board's `claim_timeout`. It is the question `karr pick`
answers by *taking* the card, so this is how to see the free work without
touching it, and it uses the very test `pick` uses. It is not the opposite of
`--claimed-by NAME`: that one is an exact match on the field and matches an
expired claim too, so the two overlap on "cards NAME no longer holds" and
passing both is a usage error. Since it asks about the claim and nothing else,
a blocked card nobody holds is still listed -- `--blocked --unclaimed` is a
real triage query.

### Show task

```bash
karr show ID
karr show                  # most recently updated task
karr show --last 5         # the 5 most recent
karr show --me             # the task you most recently acted on (re-orient)
karr show --agent NAME     # the task most recently claimed by NAME

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN

karr edit ID --title "New title"
karr edit ID --priority high --add-tag urgent
karr edit ID --add-depends-on 2,3            # append dependency ids (no duplicates; ids must exist, no self-reference)
karr edit ID --remove-depends-on 4           # absent ids are a no-op (cleanup after a deleted dependency)
karr edit ID --add-needs other-repo#7        # append a cross-board dependency (see below)
karr edit ID --remove-needs other-repo#7     # absent references are a no-op
karr edit ID --body "New description"
karr edit ID -a "Appended note"              # append to body
karr edit ID --claim agent-1                 # claim
karr edit ID --release                       # release claim
karr edit ID --block "Waiting on API"        # mark blocked
karr edit ID --unblock                       # clear blocked
```

An unknown or non-numeric id given to `--depends-on`/`--add-depends-on` rejects
the whole invocation before anything is written (usage error, exit 2); a
self-reference (`karr edit 5 --add-depends-on 5`) fails only that id, the rest
of the batch proceeds, and the command exits 1. Taking up a card whose
dependencies are unfinished warns on move/pick but is never blocked.

### Delete task

```bash
karr delete ID                               # asks first
karr delete ID --yes                         # skip confirmation
karr delete ID,ID,ID --yes                   # a batch
```

Before an id goes, `delete` names on STDERR every card on this board that

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN


### Pick next task (multi-agent)

```bash
karr pick --claim agent-1                    # pick highest priority available
karr pick --claim agent-1 --status todo --move in-progress
karr pick --claim agent-1 --tags backend
karr pick --claim agent-1 --compact          # stop after the assignment line
```

Atomically finds and claims the next available task. Respects claim timeouts, blocked state, and class-of-service priority ordering (expedite > fixed-date > standard > intangible); where two `fixed-date` cards meet, the due date is asked before prior...

### Unlock a stuck task

```bash
karr unlock                                  # list the pick locks currently held
karr unlock ID                               # break one
karr unlock --all                            # break all of them
```

`karr pick` takes a lock ref and gives it back inside the same command, so normally there is nothing here to see. An agent that dies mid-pick leaves one behind. Locks expire on their own after `lock_timeout` (default `5m`, board config); this is how ...

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN

```

A reference is `BOARD#ID`: the other board's **name** and a task id. Never a
path -- the card is shared state and two clones of the same fleet have
different directories. karr turns the name into a directory from
`--board NAME=PATH` or from the fleet config
(`~/.config/karr-foundation/config.yml`, `--fleet-config` to point elsewhere),
matching the repository's directory basename.

`--resolve` settles a link whose far card has reached one of the **far** board's
own terminal statuses, and lifts the `blocked` flag when a card's last link
settles, printing the reason it lifted. A far card that does not exist settles
nothing. A board this machine cannot place is reported, not fatal.

Like `depends_on`, a cross-board link blocks nothing by itself: `pick` hands the
card over and says what it waits on. The `blocked` flag is what keeps the card
out of `pick` and out of karr-foundation's selection -- the link is the fact,
`blocked` is the decision.

### Config

```bash
karr config                                  # show all config values
karr config get KEY                          # get a single value
karr config set KEY VALUE                    # set a writable value
karr config show --defaults                  # karr's defaults, no board read
karr config --json                           # JSON output
karr config show --compact                   # key=value per line, no padding

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN

karr config get foundation.enabled           # -> 0 or 1
karr config set foundation.enabled false     # true/false, yes/no, on/off, 1/0
karr config set foundation.reason "why"
```

### Context (board summary for embedding)

```bash
karr context                                 # print markdown summary
karr context --write-to AGENTS.md            # create/update file with sentinels
karr context --sections blocked,overdue      # filter sections
karr context --days 14                       # lookback for recently-completed
karr context --activity-limit 10             # other agents' log entries in Recent Activity
karr context --json                          # JSON output
karr context --compact                       # board_name and the four counts, key=value
```

Generates a markdown summary with sections: In Progress, Blocked, Overdue, Recently Completed, Recent Activity (other agents' log entries, newest first, bounded by `--activity-limit`, default 5). `--sections` takes the slugs `in-progress,blocked,over...

### Skill management

```bash
karr skill install                           # install skill for detected agents
karr skill install --agent claude-code       # install for specific agent
karr skill install --global                  # install globally (~/)
karr skill install --force                   # force reinstall
karr skill check                             # check if installed skills are current
karr skill update                            # update outdated skills

.claude/skills/kanban-issues-karr-cli/SKILL.md  view on Meta::CPAN

is kept separately in `refs/karr/meta/next-id`.

## Decision tree: which command?

1. **Need a board?** → `karr init`
2. **New work item?** → `karr create "Title" --priority high`
3. **What's on the board?** → `karr board` or `karr list`
4. **Starting work?** → `karr pick --claim NAME --move in-progress`
5. **Done with task, hand to review?** → `karr handoff ID --claim NAME --note "reason"`
6. **Done with task, close it?** → `karr edit ID --release && karr move ID done`
7. **Blocked?** → `karr edit ID --block "reason"`
8. **Need details?** → `karr show ID`
9. **Soft-delete?** → `karr archive ID`
10. **Board snapshot for agent context?** → `karr context --write-to AGENTS.md`
11. **Check/change config?** → `karr config` / `karr config set KEY VALUE`
12. **Install agent skills?** → `karr skill install`
13. **Need a full board snapshot?** → `karr backup` / `karr restore --yes`
14. **Need shared non-task workflow data?** → `karr set-refs` / `karr get-refs`
15. **Board should never be drained by an automation host?** → `karr disable --reason "why"`
16. **Need to remove the board completely?** → `karr destroy --yes`
17. **Overview of every board under a directory?** → `karr dashboard`

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN

# Asked with nothing in between, because $@ and $! are the whole of the
# evidence and both are global.
#
# $@ rather than errno: IO::Socket writes 'connect: timeout' there, and only
# there, when its own select() ran out -- measured, against a host that drops
# SYNs, where $! is ETIMEDOUT, which the kernel also produces on its own after
# two minutes with no Timeout set at all.
#
# EAGAIN is the second shape and belongs to unix:// alone. Measured against a
# listener whose backlog is full: with no Timeout the connect blocks
# indefinitely (still blocked after 8s), and with one it fails at once with
# EAGAIN, because IO::Socket does the timed connect non-blocking and an
# AF_UNIX connect has no in-progress state to wait on. So on that transport
# the option does not wait, it refuses -- but a connect that failed with
# EAGAIN is still one the bound ended, and reporting it as anything else would
# name a cause the caller cannot act on.
sub _connect_expired {
  my ($self, $timeout) = @_;

  return 0 unless $timeout;
  return 1 if defined $@ && $@ =~ /connect: timeout\z/;

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN

# matter is sysread rather than read: IO::Socket::SSL's sysread is a single
# Net::SSLeay::read, one record, while its read is ssl_read_all on a blocking
# socket, which is the same fill semantics this is here to get away from.
#
# A short positive read is never an end of stream and never an expiry. Over
# TLS it is the normal case, one plaintext record at a time; over a plain
# socket it is whatever the kernel had. Both are data.
#
# SSL_WANT_READ and SSL_WANT_WRITE are deliberately not retried. On a blocking
# socket they arrive as EWOULDBLOCK (IO::Socket::SSL's _skip_rw_error does
# `$! ||= EWOULDBLOCK`) and mean the underlying receive would have blocked --
# which, with SO_RCVTIMEO in force, is the bound firing and nothing else.
# Retrying would be a busy loop on WANT_READ and could not make progress on
# WANT_WRITE in any case, so they are reported as the timeout they are.
sub _pull {
  my ($self, $sock, $ctx) = @_;

  my $buf = $self->_read_buffer($sock);

  while (1) {
    # errno immediately before, errno immediately after, nothing in between:

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN

=item * C<headers> names are validated, not sanitised; see
L</"Header names are rejected, header values are stripped">

=back

=head2 Bounding a request that never ends

Nothing above stops a request waiting forever. C<Connection: close> asks the
daemon to hang up when it is done, and the readers wait for that -- so a
daemon that has nothing more to send and does not hang up leaves the client
blocked with no way out. That is not hypothetical: attaching to a container
that has B<already exited> answers, delivers the buffered frames and then
holds the connection open indefinitely on rootless Podman (karr k52), and
C</containers/{id}/stats> opened on a running container does not end when that
container exits on Docker -- it degrades into zero-filled readings and keeps
going (karr k59).

L</read_timeout> bounds that:

    # Give up after two seconds of silence rather than waiting forever.
    my $frames = $docker->containers->using(read_timeout => 2)->attach($id);

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN


=over

=item * C<tcp://> -- a real bound. Against a host that drops SYNs, an unbounded
connect waits for the kernel's own timeout, which on Linux is over two
minutes; C<< connect_timeout => 2 >> gave up after 2.00s. This is the case the
option exists for.

=item * C<unix://> -- a bound, but it does not wait. A connect to a Unix socket
whose listen backlog is full blocks: measured against a listener with
C<< Listen => 1 >> and nobody accepting, still blocked after 8 seconds. With a
C<connect_timeout> set it fails at once instead, with C<EAGAIN> -- because
C<IO::Socket> performs a timed connect non-blocking, and an C<AF_UNIX> connect
has no in-progress state to wait on. So the hang is gone, at the price of not
tolerating even a momentary backlog. A socket path that does not exist is
C<ENOENT> either way and is not affected.

=item * TLS -- bounds the TCP connect only. The handshake that follows it runs
on the connected socket, before L</read_timeout>'s C<SO_RCVTIMEO> is applied,
and is not covered by either.

lib/API/Docker/Type/SwarmInfo.pm  view on Meta::CPAN

use namespace::clean;


docker node_id => Str, wire => 'NodeID';


docker node_addr => Str;


docker local_node_state => Str,
  enum => [ '', 'inactive', 'pending', 'active', 'error', 'locked' ];


docker control_available => Bool;


docker error => Str;


docker remote_managers => [ 'PeerNode' ];

lib/API/Docker/Type/SwarmInfo.pm  view on Meta::CPAN

name would produce C<NodeId>.

=head2 node_addr

IP address at which this node can be reached by other nodes in the swarm.
The daemon defaults it to .

=head2 local_node_state

Current local status of this node. The swagger enumerates the empty string,
C<inactive>, C<pending>, C<active>, C<error> and C<locked>.

=head2 control_available

Undocumented upstream. A boolean, defaulted to C<false> upstream and C<true>
in the example, standing beside L</local_node_state> and L</managers>.
Measured against Podman 5.8.4 (API 1.44), C<GET /info> answers a complete
C<Swarm> block -- C<ControlAvailable> C<false>, C<LocalNodeState>
C<inactive> -- on an engine running no swarm at all.

=head2 error

t/connect_timeout.t  view on Meta::CPAN

  my $err = $@;
  ok !(ref $err && $err->isa('API::Docker::Error::Timeout')),
    'not turned into a timeout by having a connect_timeout set';
  like "$err", qr/Cannot connect to Unix socket/,
    'the diagnosis is the one the caller can act on';
};

# ---------------------------------------------------------------------------
# The one end-to-end assertion. An AF_UNIX connect blocks in exactly one
# situation -- the listener's backlog is full and nobody is accepting --
# measured with Listen => 1 and no accept: still blocked after 8 seconds. So
# the backlog is filled here, and then the bound has something to bound.
subtest 'the real socket: a connect that would block raises the timeout'
  => sub {
  my $dir  = tempdir(CLEANUP => 1);
  my $path = $dir . '/backlog.sock';
  my $srv  = IO::Socket::UNIX->new(Local => $path, Listen => 1)
    or plan skip_all => "cannot listen on a Unix socket here: $!";

  # Filled with the same bounded connect the code under test uses, so this
  # loop cannot be the thing that hangs. Nothing ever accepts on $srv.

t/connect_timeout.t  view on Meta::CPAN

      Peer => $path, Type => SOCK_STREAM, Timeout => 1);
    unless ($c) {
      $full = ($! == Errno::EAGAIN() || $! == Errno::EWOULDBLOCK()) ? 1 : 0;
      last;
    }
    push @held, $c;
  }
  plan skip_all => 'the listen backlog does not fill the way this platform '
    . 'was measured to' unless $full;

  my $blocked = API::Docker->new(
    host            => 'unix://' . $path,
    api_version     => '1.41',
    connect_timeout => 1,
  );

  # The alarm is the harness's own bound, not the one under test: without the
  # Timeout on the constructor this connect blocks forever, and a test that
  # hangs reports nothing. With it, the alarm never fires.
  my $t0 = time;
  eval {
    local $SIG{ALRM} = sub { die "the connect was never bounded at all\n" };
    alarm 10;
    $blocked->get('/probe');
    alarm 0;
    1;
  };
  my $err = $@;
  alarm 0;
  my $elapsed = time - $t0;

  isa_ok $err, 'API::Docker::Error::Timeout';
  is ref $err && $err->phase, 'connect',
    'the phase says the daemon was never reached';

t/spec_to_type.t  view on Meta::CPAN


subtest 'stage has nothing left to write' => sub {
  # The end state of karr k79 step 5: every class the spec calls for is in
  # lib/, so the generator's creating half has no work. A number other than
  # zero here means the spec grew a definition and nobody noticed -- which is
  # the drift checker's report, arrived at from the other side.
  my $out = qx{$^X \Q$SCRIPT\E --stage \Q$stage\E/nothing 2>&1};
  like $out, qr/rendered\s+0 class\(es\)/,
    'no class in the spec is missing from lib/';
  unlike $out, qr/NEEDS A/,
    'and nothing is blocked waiting for a name or an abstract';
};

subtest 'a name with a run of capitals must be in the map' => sub {
  # Silently guessing is how `device_i_ds` would reach a hundred classes at
  # once: the derivation produces it, and it survives the round-trip check
  # that catches every other bad name.
  my $names = File::Spec->catfile($stage, 'names.yaml');
  open my $in, '<', File::Spec->catfile($ROOT, 'maint', 'spec-to-type-names.yaml')
    or die $!;
  open my $out, '>', $names or die $!;



( run in 2.531 seconds using v1.01-cache-2.11-cpan-800906f7e73 )