App-karr

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

      never looked at the card again, so it claimed tasks that had been taken
      in the meantime: 12 parallel picks on a fresh 12-task board told nine
      agents they owned task 1, while the card named only the last of them. The
      lock was not the hole — its holder identity is the clone's `user.email`,
      which every agent on one machine shares, so all 12 acquired it quite
      legitimately. Each candidate is now re-read from its ref under its lock,
      re-tested with the same predicate, and written back under a
      compare-and-swap on the OID it was read from; an agent that loses that
      swap picks nothing and moves on. Verified with 12 forked contenders
      behind a barrier: 12 picks, 12 different tasks, and every agent named on
      the card it was told it got.
    - Fixed one orphaned lock ref bricking every command on the board.
      `list_task_refs` matched `refs/karr/tasks/N/lock` as well as `.../data`,
      so a lock left behind by an agent that died mid-pick made its task id
      exist after the card was deleted; `load_tasks` mapped that id to undef
      and `list`, `board`, `materialize` and `pick` all died on it, with no way
      out short of `git update-ref -d`. Only the data ref makes a task exist
      now, and the board list never contains undef.
    - `karr pick` no longer publishes its lock to the remote or strands its log
      entry. The lock was released, and the pick logged, after the push, so the
      remote kept the lock ref forever and the activity-log entry never left
      the clone. Both now happen before the push.
    - Locks expire. An agent that died between acquiring and releasing left a
      lock nothing could ever clear, and its task stayed unpickable forever. A
      lock older than the new `lock_timeout` board setting (default `5m`) may
      be taken over, itself by compare-and-swap against the revision whose age
      was judged, so a holder that refreshes in between is never silently
      evicted. This is deliberately not `claim_timeout` (default `1h`): a claim
      covers a work session, a lock covers one pick.
    - New command `karr unlock`: with no arguments it lists the pick locks
      currently held, with their holder, age, and whether they have expired;
      given task ids or `--all` it breaks them. The manual escape hatch for a
      stuck board, and the only one on a board that sets `lock_timeout` to
      `0s`. Breaking a lock cannot corrupt a concurrent pick — the claim is
      bound by the compare-and-swap on the card, not by the lock.
    - Board ref commits carry the time they were written. The git signature was
      built once and cached for the life of the process, so every ref a
      long-running driver (`karr-foundation`) wrote was stamped with the time
      of its first write.
    - Fixed ordinary ref contention aborting commands with a raw libgit2 error
      ("failed to lock file '.../lock.lock' for writing") followed by a stack
      trace of module paths and line numbers. Losing the race for a ref's lock
      file is now retried with a randomised backoff, and a ref write that
      genuinely fails reports a single karr-level line.
    - Raised the minimum Git::Native to 0.004 and Git::Libgit2 to 0.005. Those
      releases add compare-and-swap reference updates (`expected_old`) and
      per-ref outcomes from fetch/push, which karr needs to make ID allocation
      and lock acquisition atomic and to notice a server-rejected push. They
      also fix git+ssh remotes under libgit2 < 1.7 by verifying the hostkey
      against `~/.ssh/known_hosts`.
    - Fixed a frontmatter value ending in `---` corrupting the task and
      bricking the board. The closing delimiter was not anchored to the start
      of a line, so a value that merely ended in `---` — `karr edit 1 --block
      "waiting ---"` was enough, and YAML dumps such a value unquoted — cut the
      frontmatter mid-line. Every command that loads the board then died with
      "Missing required arguments: id, title", `delete` included, so the board
      could not be repaired with karr at all. The parser now scans for `---` at
      a line start, matching kanban-md.
    - Fixed UTF-8 being encoded twice everywhere. karr passed YAML::XS::Dump
      output (octets) around as characters, mixed that with character-level
      file I/O, and never decoded `@ARGV`. Non-ASCII text was therefore stored
      mojibaked in the refs, handed to agents mojibaked through `--json`,
      written three encodes deep by `materialize`, and destroyed by
      `backup`/`restore`; a correctly encoded kanban-md task file could not be
      imported at all ("invalid trailing UTF-8 octet"). `karr show` looked
      right only because two errors cancelled out. karr now keeps character
      strings internally and encodes only at its edges — argv, stdout/stderr,
      Git ref blobs, YAML and JSON — so non-ASCII titles, bodies, tags, and
      board names round-trip and kanban-md interop works outside ASCII.
    - Boards written by earlier versions keep working and are read correctly:
      the double encoding is undone on load for any board without the new
      `refs/karr/meta/encoding` marker. New command `karr repair` makes that
      permanent — it reports by default, rewrites the affected refs with
      `--yes`, and stamps the marker so nothing guesses at the board's bytes
      again. It is idempotent, never rewrites a ref whose payload is ASCII, and
      preserves task timestamps. `karr init` and `karr import --yes` stamp the
      marker themselves.
    - Fixed a runaway that could take the whole machine down: after a command
      died (a plain usage error was enough), the sync guard was only reaped in
      Perl's global destruction and pushed from there. libgit2 is reached
      through FFI::Platypus, whose type parser and library-search tables are
      already being freed in that phase, so the push re-entered them and
      recursed without bound — observed at 53 GB RSS on a 62 GB box, killable
      only from outside. It needed a board with a remote to trigger, which is
      why boards without one never showed it. App::karr::Git now refuses every
      native operation while `${^GLOBAL_PHASE}` is `DESTRUCT`, so the whole
      class of teardown re-entry degrades into an ordinary error.
    - Behaviour change on the die path: a sync guard reaped in global
      destruction reports instead of pushing. When refs were written but never
      pushed it now prints "Local refs are intact. Run 'karr sync' to push
      them."; when the command died before writing anything it stays silent.
      The automatic insurance push promised for this window never actually
      worked (it either recursed as above or was lost as an "(in cleanup)"
      warning) — making it deterministic is what exposed that. Restoring a real
      push is tracked separately.
    - And now restored: a command that dies after writing refs pushes them
      before the process exits again, instead of only advising `karr sync`.
      Armed sync guards register in a process-wide registry that `karr` drains
      from an `END` block — the last point at which pushing is still safe, and
      one that also covers the `exit` calls inside command bodies. A command
      that died before writing anything still pushes nothing and says nothing,
      and a push that fails there warns without touching the exit code. The
      global-destruction report above stays as the last resort for embedders
      that never drain the registry.
    - A writing command whose push fails no longer retries six times. Its
      `sync_after` disarms the guard after spending its own three attempts, so
      the new `END` flush does not repeat the identical failing push on a
      command that is already reporting the failure.
    - `karr skill show` no longer warns "Wide character in print". The bundled
      skill file is read decoded, so it is now encoded back to UTF-8 bytes at
      the one print site. The output bytes were always correct, but the warning
      was noise on stderr — and it ended up inside the written file whenever
      someone refreshed an installed SKILL.md with `karr skill show >file 2>&1`.
    - The installed executables `karr` and `karr-foundation` now carry a
      `$VERSION`. Both shipped versionless through 0.400, 0.401 and 0.402: the
      woven POD had a VERSION section (generated from the dist version), but the
      code itself declared none. Dist::Zilla only inserts a `$VERSION` into a
      file that has a `package` statement, which a script does not — so the
      line has to exist once, after which every release keeps it in step.
    - Fixed silent loss of another agent's work on every shared board. `push`
      sent `refs/karr/*` with a forced refspec but `pull` fetched with a
      non-forced one, and since karr writes every board ref as a parentless
      commit, no update is ever a fast-forward. libgit2 declined the update
      without reporting an error, so `karr sync` said "Done." and exited 0 while
      the local ref stayed stale — and the next write force-pushed that stale
      ref over the other agent's version. A pull now really applies what the
      remote has, so the board settles on last-writer-wins with no stale read in
      between. The first sync of a task always worked, which is why this hid.
    - A task deleted in one clone stays deleted instead of being resurrected by
      the next clone that writes. Push already pruned, so `karr delete` was not
      durable in any multi-clone setup.
    - `pull` no longer fetches straight into the board. The remote state lands
      in a per-remote tracking mirror under `refs/karr-remote/`, and the board is
      reconciled against it. That mirror is what tells a ref the remote deleted
      apart from one that only exists locally because its push failed — the
      first is pruned, the second is kept, which is what karr promises with
      "Local refs are intact. Run 'karr sync' to retry.". It also means a board
      that `karr init` created and never pushed, and a clone that has never
      fetched, both survive their first sync untouched.
    - A ref that changed on both sides since the last sync is no longer resolved
      in silence. The remote version takes the slot, the local one is kept under
      `refs/karr-conflict/` and a warning names both, so the overwrite is
      reported and recoverable rather than invisible. Neither `refs/karr-remote/`
      nor `refs/karr-conflict/` is ever pushed or shown on the board.
    - Helper refs (`karr set-refs` / `get-refs`) are fetched forced for the same
      reason, so `karr get-refs` no longer serves a stale local copy after the
      ref changed on the remote.
    - The git-CLI transport fallback and libgit2 no longer disagree on a
      diverged board. The fallback used to fail with "non-fast-forward" on every
      ref, which made every writing command fail permanently — including `karr
      sync`, so the clone could only be recovered with raw git. Both transports
      now take the same refspec, and the reconciliation that follows the fetch
      is the same code either way, so they reach the same board state.
    - The git-CLI transport no longer reports success when `git` dies from a
      signal. It checked `$? >> 8`, which is 0 for a signal death as well as for
      a clean exit, so a `git` stopped by the OOM killer, a Ctrl-C on the process
      group or a SIGPIPE was announced as a completed transport while the remote
      received nothing. The signal is now reported as a failure, with its number.
    - The git-CLI transport no longer deadlocks past 64 KiB of stderr. It read
      stdout to EOF before touching stderr, so once the child filled the stderr
      pipe buffer neither side moved again — reachable with roughly 700 rejected
      refs, and possible after the command had already printed its result. Both
      streams are drained together now, and a run is bounded by a wall-clock
      timeout (120s by default, `KARR_TRANSPORT_TIMEOUT` overrides, `0` disables)
      after which the child is killed and the transport reported as failed.
    - `set-refs`, `get-refs` and `agent-name` are reachable again with `--dir`.
      The dashed spellings were rewritten to their internal command names only
      at `$ARGV[0]`, so `karr --dir PATH get-refs REF` — the shape an
      orchestrator driving karr from outside the target repository uses — died
      with "Unknown command: get-refs". They are now registered as command names



( run in 1.359 second using v1.01-cache-2.11-cpan-364913b4093 )