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 )