App-karr

 view release on metacpan or  search on metacpan

docs/superpowers/specs/2026-05-15-role-boardaccess-split-design.md  view on Meta::CPAN

    my ($ok, $err) = (1, '');
    for my $attempt (1 .. 3) {
        my $git = $self->git;
        my $msg = "Push attempt $attempt of 3...";
        # push happens here
        if ($ok) { $self->{_done} = 1; return; }
        push @{$self->{_errors}}, $err;
        print STDERR "$msg failed: $err\n";
        sleep 1 if $attempt < 3;
    }
    die "Push failed after 3 attempts. Local refs are intact.\n"
      . "Run 'karr sync' to retry.\n"
      . "Errors: " . join(', ', $self->errs) . "\n";
}
```

Commands use:
```perl
sub execute {
    my ($self, $args_ref, $chain_ref) = @_;
    my $guard = $self->sync_before;  # backup insurance

    # ... command logic ...
    # may die/croak, guard handles cleanup in DESTROY

    $self->sync_after;  # explicit call for clarity
    undef $guard;       # mark done, DESTROY no-ops
}
```

### No Materialized Temp Dir

Commands operate directly on refs via `$store->load_tasks()`, `$store->save_task($task)`, etc. No intermediate `.md` files.

`tasks/` directory is:
- In `.gitignore` (never committed)
- Generated on demand via `karr materialize` as a human-readable view
- Warning issued if `tasks/` exists and is not in `.gitignore`

### Benefits

- **Locality**: Sync lifecycle is in one role. New commands can't forget it.
- **Leverage**: Commands become unit-testable — mock `store` and `git`, no temp dir needed.
- **Depth**: `Role::BoardDiscovery` is small, `Role::SyncLifecycle` is single-purpose.
- **Deletion test**: If `Role::SyncLifecycle` were deleted, the 3-retry push logic would reappear in every command. If `Role::BoardDiscovery` were deleted, git_root/store discovery would reappear in every command.

## Files Affected

- Create `lib/App/karr/Role/BoardDiscovery.pm`
- Create `lib/App/karr/Role/SyncLifecycle.pm`
- Create `lib/App/karr/SyncGuard.pm`
- Modify `lib/App/karr/Role/BoardAccess.pm` — may be removed or kept as empty alias for backward compat
- Modify all 20+ command modules to compose `Role::BoardDiscovery` + `Role::SyncLifecycle`
- Modify `App::karr` (main app) to compose roles
- Update `.gitignore` to ensure `tasks/` is ignored
- Update `AGENTS.md` and `CLAUDE.md` to reflect new architecture

## Risks

- `sync_after` called twice if command calls it explicitly AND guard DESTROY runs — mitigated by `_done` flag
- Commands that currently read `.md` files directly need to be updated to use `$store->load_tasks()`



( run in 0.319 second using v1.01-cache-2.11-cpan-bbe5e583499 )