App-karr

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

      from a signal. The OOM-killer, an external SIGTERM, a SIGSEGV, and
      any other signal-death shape were all booked as a clean run:
      `last_error` stayed unset, the cooldown that exists to back off
      after a machine-killing agent never engaged, and the next cron
      tick re-launched at full rate. The fix reads both halves of `$?`:
      signal death becomes `128 + signum` (the shell convention, so
      SIGTERM=143, SIGKILL=137, SIGSEGV=139, SIGINT=130); a normal exit
      falls through to `( $? >> 8 ) & 255`. The timeout path's exit code
      already used this convention; the classifier now matches it for
      every signal, including the ones we don't fire ourselves.

    - `karr pick` ranks candidates and `karr context --sections in-progress`
      lists in-progress tasks by the board's own `priorities` and `classes`
      lists, not by a hardcoded table (ticket k149). `Cmd/Pick.pm` used to
      sort through `App::karr::Config->priority_order` and `->class_order` —
      class methods that only knew the four default priorities and four
      default classes. On a board imported from kanban-md with a longer
      priorities list (or any class name the table did not recognise),
      every unknown priority collapsed into the `// 2` fallback, the sort
      became a no-op on that axis, and the wrong card went out — silently
      and consistently, while `karr list --sort priority` showed the right
      order right next to it. The reproduction in the ticket was a board
      with priorities `[low, medium, high, critical, blocker]`: `karr pick`
      handed out the merely-critical card because `blocker` was unknown to
      the table, and `karr context --sections in-progress` listed
      `critical` above `blocker` for the same reason. The fix reads
      `$self->config->priorities` and `$self->config->classes` and sorts
      by class index (lower = more urgent) then by priority index (higher
      = most urgent), matching kanban-md's `internal/board/pick.go:74-90`.
      `App::karr::Config->priority_order` and `->class_order` are removed
      rather than left in place: their only callers were the two bug sites,
      and leaving them around invites a future caller to use the wrong one
      again. The `=attr priority` / `=attr class` POD in `Task.pm` now
      references the `Config/priorities` and `Config/classes` instance
      methods.

    - `karr edit --status X --release` no longer walks straight through the
      `require_claim` guard and lands a card in a require_claim column with
      no claim on it (ticket k150). `Cmd/Edit.pm` cleared the claim forty
      lines after `apply_status_change` had already satisfied the guard with
      that same claim, so `karr edit 1 --claim agent-a` followed by
      `karr edit 1 --status in-progress --release` produced an unowned
      in-progress card — a state `karr move` and `karr edit --status` both
      refuse to create by any other route. `karr edit 2 --status in-progress
      --claim agent-b --release` did it in one command. The fix has two
      pieces: `--claim` and `--release` are rejected together at the flag
      layer as a usage error (exit 2), matching kanban-md
      (`cmd/edit.go:128-130`); and the `--release` block now runs before
      `apply_status_change`, so the guard in
      `Role::TaskMutation::apply_status_change` sees the post-release state
      and refuses the status change, matching kanban-md's `validateEditPost`
      firing after `applyFn` regardless of release
      (`internal/board/mutate.go:442`). `--release` alone on a card already
      in a require_claim column is intentionally left unchanged: it is the
      same shape but outside the ticket's scope (kanban-md has the same
      hole).

    - karr-foundation no longer throws away a successful agent run because of
      something it printed (ticket k160). The common-error scan ran on every
      run before anything asked whether the run had worked, over the whole
      transcript, against bare substrings — network, quota, credentials, 401,
      403, 429, 503. An agent working a karr board prints the board, so a
      backlog line reading "retry the network fetch on 503" matched, and so
      did a diffstat of 403 changed lines. The drain aborted, the cards the
      agent had just moved were credited to nobody, and the cooldown climbed
      1m, 2m, 4m … 64m without ever resetting, because the next run printed
      the same words: a healthy board throttled to one discarded run per hour.
      What a run did is now asked before what it printed. A run that exited 0
      and moved the board is progress whatever scrolled past, and is never
      reclassified by its own output; the scan is evidence only where there is
      none other, a run that moved nothing — which is what a rate-limited or
      unauthenticated agent looks like. A pattern seen in a run that did move
      the board is noted in `.karr.log` and otherwise ignored. The default
      patterns are narrow to match: a symptom word counts next to a failure
      word on the same line ("network error", "invalid credentials", "quota
      exceeded"), never on its own, and an HTTP status only where something
      adjacent marks it as one ("API error: 429", "429 Too Many Requests") —
      not in a diffstat, a byte count, a line number or a commit hash. Genuine
      failures reported by an agent that still exits 0 keep triggering the
      backoff, which is what the scan is for. A board's own `error_patterns`
      are unchanged: plain case-insensitive substrings.

    - `.karr.state` no longer keeps a `last_error` from a run three cooldowns
      ago sitting next to `last_exit: 0` with nothing to explain the pair
      (ticket k160). `last_error` describes the last run and is dropped by the
      next run that is not a common error. Where the pair is real — an agent
      that reports a rate limit and still exits 0 — it is now said out loud:
      `.karr.log` records "COMMON-ERROR rate limit — agent exited 0, run
      discarded", and `karr-foundation --status` names the reason beside the
      wait ("cooldown 240s (rate limit)").

    - Fixed data loss when a pull could not write a ref (ticket k154). The
      apply step of the reconciliation used an unretried ref write whose
      failure nobody checked, so a ref whose `.lock` file was held — by
      another karr mid-write, or left behind by one that was killed — was not
      applied, while the `refs/karr-remote/` mirror was advanced as if it had
      been. The next reconciliation then read the stale local ref as unpushed
      work and the forced, pruning push wrote it over the remote's newer card,
      in every clone, at exit 0. Those writes now retry on the same terms as
      every other ref write in `App::karr::Git`, a ref that still cannot be
      applied leaves the mirror at its pre-fetch value so the next sync
      decides it again, and the pull fails with a non-zero exit naming the ref
      instead of proceeding to the push. The same fix covers a remote deletion
      that could not be applied (which used to be pushed back as a
      resurrection), a conflict whose local version could not be parked (the
      local version is now kept rather than replaced), a mirror rollback
      behind a refusal that only half succeeded (now reported), and the
      board-identity stamp the mirror could not record.

    - karr-foundation no longer auto-blocks tasks its agent never touched
      (ticket k158). `_stuck_tasks` claimed to return "tasks the agent engaged
      (claimed / in-progress) but did not move" and tested only whether the
      card carried *any* claim or sat in `in-progress` — who held it was never
      compared against anything. Every drain iteration in which the agent moved
      some other card therefore charged an attempt against every card somebody
      else was holding, and since `max_attempts` (default 2) can be spent
      inside a single drain, a human's in-progress card was blocked with
      `auto-block: no progress after N attempts (foundation)` and pushed to the
      remote within seconds — a destructive write to shared board state about
      work foundation never attempted, dropping that card out of `karr pick`'s
      actionable set behind its owner's back and giving a reason that is
      factually wrong. Engagement is now proven rather than assumed: foundation
      runs the agent with `KARR_ROLE=agent`, so the agent's `karr` writes are
      recorded in the board's own activity log under the `agent` identity, and
      only cards named there during that drain — held by nobody, or under a
      claim name the agent itself wrote with — can be penalized. A card the
      agent merely left claimed in an earlier run no longer counts either; a
      stale claim is what `claim_timeout` and `karr unlock` are for. Where that
      evidence is missing altogether — an agent command that never calls
      `karr`, an unreadable log — foundation now auto-blocks nothing rather
      than guess: a drain that ends on its iteration cap costs an iteration,
      blocking the wrong card costs somebody their work. The ownership test is
      repeated at the write itself, which is the only place foundation mutates
      a board, so a future caller inherits the guarantee instead of having to
      remember it.



( run in 0.950 second using v1.01-cache-2.11-cpan-007c89162af )