App-Raider

 view release on metacpan or  search on metacpan

.claude/skills/perl-io-async-future/SKILL.md  view on Meta::CPAN

}
```

Right:

```perl
sub _reconnect_attempt {
    my ($self) = @_;
    return if $self->{_connected};

    weaken(my $weak = $self);

    $self->{_reconnect_future} = $self->loop
        ->delay_future(after => $self->{reconnect_wait})
        ->then(sub {
            my $self = $weak or return Future->done;
            return Future->done if $self->{_connected};
            return $self->connect;
        })
        ->on_done(sub {
            my $self = $weak or return;

.claude/skills/perl-io-async-future/SKILL.md  view on Meta::CPAN

        ->on_fail(sub {
            my $self = $weak or return;
            delete $self->{_reconnect_future};
            $self->_reconnect_attempt;        # try again
        });
}
```

**Rules:**
- Store the *whole chain* on the object (`$self->{_reconnect_future}`), not just the leaf.
- `weaken` `$self` inside callbacks — otherwise the chain holds the object alive forever.
- Always `delete $self->{_reconnect_future}` in both `on_done` and `on_fail`, or you'll guard out future reconnects with `return if $self->{_reconnect_future}`.
- On disconnect, **cancel the old `_connect_future`** so its async sub unwinds:
  ```perl
  if (my $f = delete $self->{_connect_future}) {
      $f->fail("disconnected: $reason") unless $f->is_ready;
  }
  ```

---

.claude/skills/perl-io-async-future/SKILL.md  view on Meta::CPAN

| Situation | Use |
|---|---|
| Subclassing IO::Async object | `parent 'IO::Async::Notifier'`, override `configure` |
| Storing async state on $self | `$self->{_foo_future}` — never bare lexicals |
| Sequential dependent ops | `->then` / `async`+`await` |
| Parallel, all required | `Future->needs_all` |
| First-to-finish race | `Future->wait_any` |
| Add timeout to op | `wait_any($op, $loop->delay_future(after=>N)->then_fail('timeout'))` |
| Side-effect observation | `->on_done` / `->on_fail` (don't chain) |
| Truly fire-and-forget | `->retain` (rare — usually you should hold it) |
| Closure capturing $self | `weaken(my $weak = $self)` + null-check inside |
| Cancelling stale attempt | `delete $self->{_f}; $f->cancel unless $f->is_ready` |
| Sync block-wait (tests/scripts) | `$f->get` |

---

## Common Pitfalls (the recurring ones)

- **Local-variable-only Future** → silent GC. Hold it on `$self`.
- **Async sub whose caller drops the Future** → "lost its returning future". Hold the result.
- **Strong `$self` capture in callback chain** → object never destroyed; reconnect loops leak. `weaken` it.
- **Forgetting to `delete` the held Future on completion** → stale guards block future operations.
- **Not chaining `SUPER::configure`** → defaults silently missing, or unknown keys silently accepted.
- **Returning a Future from `async sub` without `await`** → double-wrapped result.
- **Calling `$self->loop` before `$loop->add($self)`** → `loop` is undef.
- **Mixing `then` and `on_done` thinking they're the same** → `on_done` returns the original Future, your "chain" is actually two parallel observers.
- **Cancelling a Future inside its own callback** → undefined; cancel from outside.
- **Using `Future->new` instead of `$loop->new_future`** when you need loop-aware behavior (the loop variant integrates with timeouts and is the recommended form inside Notifier subclasses).

---



( run in 1.914 second using v1.01-cache-2.11-cpan-39bf76dae61 )