Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3889d3d
fix(agent-run): make yielded runs resumable
Sep 22, 2026
f6f2ee2
fix(parallel-issues): enforce auto-review coverage
Sep 22, 2026
7f95e68
fix(review-remote-pr): bind repair logs to pushed head
Sep 22, 2026
3858a20
feat(review-providers): accept generic provider names
Sep 22, 2026
401adb9
test(agent-run): pin active verification handle
Sep 22, 2026
8d601a8
merge: incorporate issue 874 verification fix
Sep 22, 2026
9b00d8f
fix(review-providers): reject fixed login overrides
Sep 22, 2026
167773c
fix(parallel-issues): preserve review handoff evidence
Sep 22, 2026
729dcd2
chore: merge issue 873 into issue 875
Sep 22, 2026
1cb645a
fix(agent-run): bound active lease lifetime
Sep 22, 2026
d9eb614
fix(review-remote-pr): repair review handoff contracts
Sep 22, 2026
6ee70e5
merge: incorporate issue 874 review repairs
Sep 22, 2026
3f89ab6
refactor(review-remote-pr): preserve skill size ratchet
Sep 22, 2026
7242b3f
fix(review-remote-pr): retain verification recipe anchors
Sep 22, 2026
f0bb990
chore: merge final issue 873 head into issue 875
Sep 22, 2026
372ef66
merge: integrate issue 875
Sep 22, 2026
b37a0fa
test(size): record integrated helper total
Sep 22, 2026
1a3293a
Merge commit 'bfe3b43c35daff6230ffd1107f822f968043dbdd' into feat/iss…
Sep 23, 2026
8f656bd
fix(review-providers): reserve built-in login aliases
Sep 23, 2026
896ad54
fix(parallel-issues): restore auto-review mode in Collect
Sep 23, 2026
2414884
fix(agent-run): harden verification boundaries
Sep 23, 2026
c00223e
merge: integrate final issue 874 repair
Sep 23, 2026
bfa525d
fix(review-remote-pr): bind evidence to checkout head
Sep 23, 2026
9b95e0d
refactor(parallel-issues): compact Collect restore
Sep 23, 2026
c0bd6d6
merge: integrate final issue 875 repair
Sep 23, 2026
785f596
test(parallel-issues): avoid ripgrep dependency
Sep 23, 2026
febb51d
merge: integrate issue 875 CI portability fix
Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agentkit/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentkit",
"version": "0.9.11",
"version": "0.9.12",
"description": "Board-aware parallel issue and PR review skills, with lifecycle hooks and a per-repository contract.",
"author": {
"name": "wrzonance",
Expand Down
2 changes: 1 addition & 1 deletion agentkit/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentkit",
"version": "0.9.11",
"version": "0.9.12",
"description": "Board-aware parallel issue and PR review skills, with lifecycle hooks and a per-repository contract.",
"author": {
"name": "wrzonance",
Expand Down
165 changes: 131 additions & 34 deletions agentkit/skills/.shared/scripts/agent-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,75 @@ if [[ -z ${BASH_VERSION:-} || ${BASH_VERSINFO[0]:-0} -lt 4 ]]; then
exit 2
fi

current_process_start() {
local pid=$1 identity
if [[ -r /proc/$pid/stat ]] && identity=$(awk '{print $22}' "/proc/$pid/stat" 2> /dev/null) &&
[[ $identity =~ ^[0-9]+$ ]]; then
printf '%s' "$identity"
return 0
fi
identity=$(LC_ALL=C ps -o lstart= -p "$pid" 2>/dev/null | tr -d '[:space:]') || return 1
[[ $identity =~ ^[[:alnum:]:]+$ ]] || return 1
printf '%s' "$identity"
}

epoch_seconds() { date -u +%s 2>/dev/null || printf '0'; }

status_agent_log() {
local requested=$1 log fallback_dir last header pid start epoch current elapsed now
[[ -f $requested && ! -L $requested && -O $requested ]] || {
printf 'agent-run: error: status requires an owned regular log: %s\n' "$requested" >&2; exit 2;
}
log=$(realpath -e -- "$requested") || exit 2
case $log in
*/.agent/logs/*.log) ;;
*)
fallback_dir=$(realpath -e -- "${TMPDIR:-/tmp}/agent-logs-$(id -u)" 2>/dev/null || true)
[[ -n $fallback_dir && $log == "$fallback_dir/"*.log ]] || {
printf 'agent-run: error: status path is not an agent log: %s\n' "$requested" >&2; exit 2;
}
;;
esac
last=$(tail -n 1 -- "$log")
if [[ $last =~ ^===\ agent-run\ exited\ rc=([0-9]+)\ after\ [0-9]+s$ ]]; then
((BASH_REMATCH[1] == 0)) && printf 'pass\n' || printf 'fail rc=%s\n' "${BASH_REMATCH[1]}"
exit 0
fi
[[ $last != '=== agent-run interrupted by '* ]] || { printf 'interrupted\n'; exit 0; }
header=$(sed -n '2p' -- "$log")
pid=$(sed -n 's/.* pid=\([0-9][0-9]*\) .*/\1/p' <<< "$header")
start=$(sed -n 's/.* process-start=\([^ ]*\) .*/\1/p' <<< "$header")
epoch=$(sed -n 's/.* epoch=\([0-9][0-9]*\) .*/\1/p' <<< "$header")
current=$(current_process_start "$pid" 2>/dev/null || true)
if [[ -n $pid && -n $start && $current == "$start" && $epoch =~ ^[0-9]+$ ]]; then
now=$(epoch_seconds); elapsed=$((now - epoch)); ((elapsed >= 0)) || elapsed=0
printf 'running pid=%s elapsed=%ss\n' "$pid" "$elapsed"
else
printf 'interrupted\n'
fi
exit 0
}

if [[ ${1:-} == status ]]; then
(($# == 2)) || { printf 'agent-run: error: usage: agent-run.sh status LOG\n' >&2; exit 2; }
status_agent_log "$2"
fi

usage() {
cat <<'EOF'
Usage: agent-run.sh [--dir PATH] [--label NAME] [--resolve NAME] [--force] [--summary] [--only NAME[,NAME...]]
Usage: agent-run.sh status LOG
agent-run.sh [--dir PATH] [--label NAME] [--resolve NAME] [--force] [--summary] [--only NAME[,NAME...]]
[--baseline-ref REF --baseline-path PATH --baseline-id ID]
(--cmd NAME | [--] <command> ...)

Runs one command with a sandbox-safe environment and a compact result summary.
status LOG Print running, pass, fail, or interrupted for an agent log.
--dir PATH Working directory for the command (default: current directory).
--repo-root PATH is a silent alias, accepted for
compatibility with the kit's other checkout-path helpers.
--label NAME Label used in the log file name (default: the command's basename).
--force Require fresh execution, including recovery of unknown evidence.
An identical in-flight local command still returns its handle.
An identical in-flight command is still refused with its log.
--summary End with status, exit code, duration, log path, digest, and receipt.
--verification-key Read-only query for one local, generic, full-checkout
command. Prints only its current fingerprint; creates no execution
Expand All @@ -41,8 +97,8 @@ Runs one command with a sandbox-safe environment and a compact result summary.
--if-declared With --cmd, exit 0 quietly when the repository declares no such
command. For a command a skill treats as optional.
--resolve NAME Query a named command without executing it. Prints declared,
runner, or unresolved and exits 0, 4, or 3 respectively; exit 2
is reserved for a fatal unsupported-interpreter guard.
runner, or unresolved and exits 0, 4, or 3 respectively. The
fatal unsupported-interpreter guard exits 2.
--cmd NAME Run the command this repository declares under that name, instead
of spelling one out. Repeatable: each --cmd runs only after the
previous one exits 0 (re-execs itself for the rest); --if-declared
Expand Down Expand Up @@ -93,7 +149,8 @@ Output:
verification miss: no reusable evidence; a following command executes freshly,
or a completed result was not stored for later reuse
verification current/reused: prior evidence, never a fresh PASS
verification running/unknown: durable handle and exit 75; inspect before retry
active duplicate exits 2 with its original log; wait instead of relaunching
verification unknown: unknown abandoned handle exits 75; inspect before retry

Examples:
agent-run.sh --cmd test
Expand All @@ -117,6 +174,7 @@ failure_result() {
"${failure_evidence:-${log_file:-stderr}}" "$failure_state" "$failure_action" >&2
fi
if declare -F cleanup_suite_run >/dev/null; then cleanup_suite_run; fi
if declare -F cleanup_active_run >/dev/null; then cleanup_active_run; fi
if ((summary_ready)); then
printf 'agent-run-summary status=%s rc=%s duration_seconds=%s log=%q log-sha256=%s receipt=%q\n' \
"$summary_status" "$status" "$elapsed" "$log_file" "$log_sha256" "$log_sha256_receipt"
Expand Down Expand Up @@ -162,6 +220,7 @@ finish() {
[[ -z ${verification_fd:-} ]] || exec {verification_fd}>&-
if ((rc == 0)) && ((${#remaining_queue[@]})); then
if declare -F cleanup_suite_run >/dev/null; then cleanup_suite_run; fi
if declare -F cleanup_active_run >/dev/null; then cleanup_active_run; fi
build_chain_argv
exec "$0" "${chain_argv[@]}"
fi
Expand Down Expand Up @@ -1051,7 +1110,8 @@ apply_test_focus() {
# --------------------------------------------------------------------- logs ---
choose_log() {
local log_dir stamp log
if [[ -n $git_top ]] && dir_writable "$git_top/.agent/logs"; then
if [[ -n $git_top && ! -L $git_top/.agent && ! -L $git_top/.agent/logs ]] &&
dir_writable "$git_top/.agent/logs"; then
log_dir=$git_top/.agent/logs
else
# Failing commands routinely echo tokens and credentialed URLs into these
Expand Down Expand Up @@ -1083,22 +1143,10 @@ concurrent_suites=1
timeout_scale=1
suite_marker_dir=${TMPDIR:-/tmp}/agent-run-suites-$(id -u)

current_process_start() {
local pid=$1
if [[ -r /proc/$pid/stat ]]; then
awk '{print $22}' "/proc/$pid/stat" 2> /dev/null
else
# macOS has no /proc; kill -0 is the portable liveness fallback. The
# marker is still short-lived and is removed by the EXIT trap.
kill -0 "$pid" 2> /dev/null || return 1
printf 'alive'
fi
}

suite_marker_live() {
local marker=$1 pid start current
read -r pid start < "$marker" 2> /dev/null || return 1
[[ $pid =~ ^[0-9]+$ && ($start =~ ^[0-9]+$ || $start == alive) ]] || return 1
[[ $pid =~ ^[0-9]+$ && $start =~ ^[[:alnum:]:]+$ ]] || return 1
current=$(current_process_start "$pid")
[[ -n $current && $current == "$start" ]]
}
Expand All @@ -1116,7 +1164,7 @@ register_suite_run() {
die "cannot create active-suite marker in $suite_marker_dir"
pid=$$
start=$(current_process_start "$pid")
[[ $start =~ ^[0-9]+$ || $start == alive ]] || {
[[ $start =~ ^[[:alnum:]:]+$ ]] || {
rm -f -- "$marker"
die "cannot identify active-suite process $pid"
}
Expand Down Expand Up @@ -1146,6 +1194,40 @@ cleanup_suite_run() {
suite_marker=''
}

active_run_handle='' active_run_fd='' active_run_owned=0
claim_active_run() {
local root key prior
[[ -n ${git_top:-} && -z $verification_handle ]] || return 0
command -v flock >/dev/null || return 0
[[ ! -L $git_top/.agent ]] || return 0
root=$git_top/.agent/run-records
assert_private_dir "$root"
key=$(printf '%s\0' "$work_dir" "${cmd[@]}" | sha256sum | awk '{print $1}')
active_run_handle=$root/$key
assert_private_dir "$active_run_handle"
[[ ! -L $active_run_handle/lock && ! -L $active_run_handle/running ]] ||
refuse_boundary "active run record is a symlink: $active_run_handle"
exec {active_run_fd}>"$active_run_handle/lock" || refuse_boundary "cannot open active run lock: $active_run_handle/lock"
if ! flock -n "$active_run_fd"; then
IFS= read -r prior < "$active_run_handle/running" 2>/dev/null || true
[[ -n $prior ]] || prior=$active_run_handle
failure_class=usage failure_state=command-already-running failure_action=wait-for-existing-log
failure_evidence=$prior
printf 'agent-run: already running: %s; wait for its exited line or run status on it\n' "$prior" >&2
finish 2
fi
printf '%s\n' "$log_file" > "$active_run_handle/running"
active_run_owned=1
}

# shellcheck disable=SC2329 # Invoked indirectly by the EXIT trap.
cleanup_active_run() {
((active_run_owned)) || return 0
rm -f -- "$active_run_handle/running" 2>/dev/null || true
[[ -z $active_run_fd ]] || exec {active_run_fd}>&-
active_run_owned=0
}

# A worker may ask for one failed verification to be checked against the chain
# base. The source path must be the same blob at both commits, and the base
# checkout must produce matching failure evidence. Deliberately opt-in: ordinary
Expand Down Expand Up @@ -1647,9 +1729,16 @@ claim_verification() {
[[ ! -L $verification_handle/running ]] || refuse_boundary "verification running record is a symlink: $verification_handle/running"
exec {verification_fd}>"$verification_handle/lock" || refuse_boundary "cannot open verification lock: $verification_handle/lock"
if ! flock -n "$verification_fd"; then
local running_log=''
IFS= read -r running_log < "$verification_handle/running" 2>/dev/null || true
failure_evidence=$verification_handle
failure_state=verification-running
failure_action=inspect-running-handle-before-retry
failure_state=command-already-running
failure_action=wait-for-existing-log
if [[ $running_log == "$git_top/.agent/logs/"*.log ]]; then
failure_evidence=$running_log
printf 'agent-run: already running: %s; wait for its exited line or run status on it\n' "$running_log" >&2
finish 2
fi
printf 'agent-run: verification running: handle=%s\n' "$verification_handle"
finish 75
fi
Expand Down Expand Up @@ -1822,25 +1911,27 @@ fi

log_file=$(choose_log)
[[ -z $verification_handle ]] || printf '%s\n' "$log_file" > "$verification_handle/running"
claim_active_run
register_suite_run
trap failure_result EXIT
process_start=$(current_process_start "$$") || die "cannot identify active process $$"

# Announce the log before captured output makes a long run look hung.
if ((summary_cmd)); then
printf 'running: %s (wait for the terminal agent-run-summary marker)\n' "$cmd_str" >&2
else
printf 'running: %s\n log: %s (grows while this runs; tail it instead of waiting blind)\n' \
"$cmd_str" "$log_file" >&2
fi
printf ' a log with no "=== agent-run exited" line has NOT finished\n' >&2
printf 'running: %s\n log: %s\n' "$cmd_str" "$log_file" >&2
printf ' if this call returns before "=== agent-run exited", the run is still going: resume this same call; never relaunch; status: %q status %q\n' \
"$0" "$log_file" >&2

# The closing marker distinguishes completed logs; exclude bookkeeping lines.
readonly LOG_HEADER_LINES=2
log_head=none log_clean=no
[[ -z $git_top ]] || log_head=$(git -C "$git_top" rev-parse --verify -q HEAD 2> /dev/null) || log_head=none
[[ -z $git_top || -n $(git -C "$git_top" status --porcelain --untracked-files=no 2> /dev/null || printf x) ]] || log_clean=yes
{
printf '=== agent-run %s\n' "$cmd_str"
printf '=== started %s pid=%s cwd=%s concurrent-suites=%s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ 2> /dev/null || printf 'unknown')" "$$" "$work_dir" \
"$concurrent_suites"
printf '=== started %s pid=%s process-start=%s epoch=%s cwd=%s concurrent-suites=%s head=%s tracked-clean=%s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ 2> /dev/null || printf 'unknown')" "$$" \
"$process_start" "$(epoch_seconds)" "$work_dir" "$concurrent_suites" \
"$log_head" "$log_clean"
} > "$log_file"

started_at=$SECONDS
Expand All @@ -1859,15 +1950,21 @@ trap 'log_interrupted SIGINT' INT
trap 'log_interrupted SIGTERM' TERM
rc=0
attempt_start_line=3
(cd -- "$work_dir" && exec "${cmd[@]}") >> "$log_file" 2>&1 || rc=$?
(
[[ -z $active_run_fd ]] || exec {active_run_fd}>&-
cd -- "$work_dir" && exec "${cmd[@]}"
) >> "$log_file" 2>&1 || rc=$?
load_flake_retry=0
if ((rc != 0)) && probe_timeout_load_flake "$log_file"; then
load_flake_retry=1
printf '=== finding load-flake: probe timeout under concurrent-suites=%s; retried 1/1\n' \
"$concurrent_suites" >> "$log_file"
attempt_start_line=$(($(wc -l < "$log_file" | tr -d '[:space:]') + 1))
rc=0
(cd -- "$work_dir" && exec "${cmd[@]}") >> "$log_file" 2>&1 || rc=$?
(
[[ -z $active_run_fd ]] || exec {active_run_fd}>&-
cd -- "$work_dir" && exec "${cmd[@]}"
) >> "$log_file" 2>&1 || rc=$?
fi
trap - INT TERM
elapsed=$((SECONDS - started_at))
Expand Down
8 changes: 4 additions & 4 deletions agentkit/skills/.shared/scripts/bootstrap-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,8 @@ done
# infer from the forge. Keep the proposal visible until a valid declaration is
# selected; a carried declaration then appears once as active config below.
review_providers_declared=0
if [[ -f $repo_root/.agent/config.env && $reset -eq 0 ]]; then
if grep -qE '^[[:space:]]*AGENT_REVIEW_PROVIDERS=(coderabbit|github-code-quality|none|coderabbit,github-code-quality|github-code-quality,coderabbit)[[:space:]]*$' \
"$repo_root/.agent/config.env" 2> /dev/null; then
if [[ -f $repo_root/.agent/config.env && $reset -eq 0 && -x $resolver ]]; then
if "$resolver" --repo-root "$repo_root" --get AGENT_REVIEW_PROVIDERS > /dev/null 2>&1; then
review_providers_declared=1
fi
fi
Expand Down Expand Up @@ -488,7 +487,8 @@ fi
fi
if ((review_providers_declared == 0)); then
printf '\n# Automated review providers expected on pull requests. Choose one or more.\n'
printf '# Supported choices: coderabbit, github-code-quality, or none (none is exclusive).\n'
printf '# Known providers get provider-specific settlement. Any other valid name is observed\n'
printf '# and triaged on the merits, and is never triggered. Use none alone to disable reviews.\n'
printf '# AGENT_REVIEW_PROVIDERS=\n'
fi
if [[ -n $proposal_inventory ]]; then
Expand Down
Loading
Loading