@cxi-lmai/ci-agent-platform 3.1.3 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cxi-lmai/ci-agent-platform",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.4",
|
|
4
4
|
"description": "Autonomous dev pipeline on plain GitLab CI or GitHub Actions, driven by Claude Code. A labeled issue goes in, an open merge request comes out.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -79,6 +79,18 @@ variables:
|
|
|
79
79
|
PIPE_ORCHESTRATE: "0" # set to "1" in the schedule to run orchestrate
|
|
80
80
|
PIPE_SPEC_TEMPLATE_PATH: ".gitlab/issue_templates/Spec.md"
|
|
81
81
|
|
|
82
|
+
# --- Agent deadline (omp harness) -----------------------------------------
|
|
83
|
+
# A job `timeout:` is a hard kill — the runner SIGKILLs the process tree and
|
|
84
|
+
# the runner script never posts its comment, metrics or marker file. So
|
|
85
|
+
# `pipe_run_omp` bounds the agent itself with `omp --max-time`, derived from
|
|
86
|
+
# GitLab's own CI_JOB_TIMEOUT minus the time already spent minus the reserve
|
|
87
|
+
# below, which leaves room for the post-agent steps. An overrun then takes
|
|
88
|
+
# the normal degraded path (failure-notice comment + metrics) instead of
|
|
89
|
+
# dying silently. Set PIPE_AGENT_MAX_TIME (e.g. "20m") to override the
|
|
90
|
+
# derivation for every job; GitHub Actions exposes no job-timeout variable,
|
|
91
|
+
# so there the flag is omitted and timeout-minutes stays the only bound.
|
|
92
|
+
PIPE_AGENT_TIME_RESERVE: "180" # seconds of the job budget kept for the post-agent steps
|
|
93
|
+
|
|
82
94
|
# --- Build / verify --------------------------------------------------------
|
|
83
95
|
PIPE_VERIFY_CMD: "" # compile + unit tests, run after a fix
|
|
84
96
|
PIPE_COMPILE_CMD: "" # compile only (defaults to PIPE_VERIFY_CMD)
|
|
@@ -212,11 +224,15 @@ variables:
|
|
|
212
224
|
|
|
213
225
|
# =============================================================================
|
|
214
226
|
# review: run the automated review on a non-draft MR that changes code.
|
|
227
|
+
# The 45m budget matches `code` and is not generous: the skill fans out to the
|
|
228
|
+
# code-reviewer plus up to three specialist subagents, and observed runs on a
|
|
229
|
+
# real project spread from 1m to over 20m with diff size. It was 20m until
|
|
230
|
+
# three consecutive cap-outs on large MRs were measured in production.
|
|
215
231
|
# =============================================================================
|
|
216
232
|
review:
|
|
217
233
|
extends: .claude-base
|
|
218
234
|
stage: review
|
|
219
|
-
timeout:
|
|
235
|
+
timeout: 45m
|
|
220
236
|
script:
|
|
221
237
|
- bash "$PIPE_SCRIPTS_DIR/review.sh"
|
|
222
238
|
artifacts:
|
|
@@ -369,6 +369,49 @@ pipe_translate_tools_to_omp() {
|
|
|
369
369
|
echo "${result#,}"
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
# --- Agent deadline -----------------------------------------------------
|
|
373
|
+
# A CI job timeout is a hard kill: the runner SIGKILLs the whole process tree,
|
|
374
|
+
# so the runner script never reaches the steps that post the comment, write
|
|
375
|
+
# the metrics record and emit the marker file. An agent that overruns
|
|
376
|
+
# therefore leaves a red job with no output at all (observed in production:
|
|
377
|
+
# three review timeouts in one week, each at the cap with the agent mid-run).
|
|
378
|
+
# Bounding the agent itself with `omp --max-time` turns an overrun into the
|
|
379
|
+
# platform's normal degraded path — failure-notice comment plus metrics —
|
|
380
|
+
# instead of a hard kill.
|
|
381
|
+
|
|
382
|
+
pipe_agent_max_time() {
|
|
383
|
+
# Echoes the --max-time value to pass to omp, or nothing when no deadline
|
|
384
|
+
# can be derived (GitHub Actions exposes no CI_JOB_TIMEOUT equivalent;
|
|
385
|
+
# there the workflow's timeout-minutes stays the only bound). An explicit
|
|
386
|
+
# PIPE_AGENT_MAX_TIME wins and is passed through verbatim, so a project can
|
|
387
|
+
# set "20m" or "1h" by hand. Otherwise the budget is the job timeout, minus
|
|
388
|
+
# the time already spent (image pull, checkout, before_script, API prep),
|
|
389
|
+
# minus PIPE_AGENT_TIME_RESERVE for the post-agent steps.
|
|
390
|
+
if [ -n "${PIPE_AGENT_MAX_TIME:-}" ]; then
|
|
391
|
+
printf '%s' "$PIPE_AGENT_MAX_TIME"
|
|
392
|
+
return 0
|
|
393
|
+
fi
|
|
394
|
+
local budget="${CI_JOB_TIMEOUT:-}"
|
|
395
|
+
case "$budget" in ''|*[!0-9]*) return 0 ;; esac
|
|
396
|
+
local reserve="${PIPE_AGENT_TIME_RESERVE:-180}" elapsed=0 started now
|
|
397
|
+
# Guard the emptiness explicitly: `date -d ""` does not fail, it silently
|
|
398
|
+
# resolves to midnight today, which would read as a many-hour-old job.
|
|
399
|
+
if [ -n "${CI_JOB_STARTED_AT:-}" ]; then
|
|
400
|
+
started=$(date -d "$CI_JOB_STARTED_AT" +%s 2>/dev/null || true)
|
|
401
|
+
if [ -n "$started" ]; then
|
|
402
|
+
now=$(date +%s)
|
|
403
|
+
elapsed=$(( now - started ))
|
|
404
|
+
[ "$elapsed" -lt 0 ] && elapsed=0
|
|
405
|
+
fi
|
|
406
|
+
fi
|
|
407
|
+
local left=$(( budget - elapsed - reserve ))
|
|
408
|
+
# A job already out of budget gets no flag: let the agent start and be cut
|
|
409
|
+
# by the runner exactly as it was before this function existed, rather than
|
|
410
|
+
# be killed one second in with a nonsensical deadline.
|
|
411
|
+
[ "$left" -lt 60 ] && return 0
|
|
412
|
+
printf '%s' "$left"
|
|
413
|
+
}
|
|
414
|
+
|
|
372
415
|
pipe_run_omp() {
|
|
373
416
|
# $1 = agent label for metrics, $2 = skill invocation (e.g. "/review-mr"),
|
|
374
417
|
# $3 = allowed tools in Claude Code naming (optional, translated
|
|
@@ -377,6 +420,14 @@ pipe_run_omp() {
|
|
|
377
420
|
# frontmatter), stdin = extra prompt (optional, usually empty).
|
|
378
421
|
local label="$1" skill="$2" tools="${3:-Agent,Read,Write,Edit,Glob,Grep,Bash}" model="${4:-}"
|
|
379
422
|
local omp_tools; omp_tools=$(pipe_translate_tools_to_omp "$tools")
|
|
423
|
+
# Bound the run below the job's own hard kill (see pipe_agent_max_time).
|
|
424
|
+
local max_time; max_time=$(pipe_agent_max_time)
|
|
425
|
+
local -a max_time_args=()
|
|
426
|
+
local max_time_txt=""
|
|
427
|
+
if [ -n "$max_time" ]; then
|
|
428
|
+
max_time_args=(--max-time "$max_time")
|
|
429
|
+
max_time_txt=$(printf ' --max-time %q' "$max_time")
|
|
430
|
+
fi
|
|
380
431
|
pipe_require_harness omp
|
|
381
432
|
local stdin_file; stdin_file=$(mktemp)
|
|
382
433
|
cat > "$stdin_file" || true
|
|
@@ -402,12 +453,14 @@ pipe_run_omp() {
|
|
|
402
453
|
printf 'cd %q\n' "$PWD"
|
|
403
454
|
printf 'source %q\n' "$SCRIPT_LIB_DIR/usage-capture-omp.sh"
|
|
404
455
|
printf 'export PIPE_CAPTURE_MODEL=%q\n' "$model"
|
|
456
|
+
# The %s after `--` is the optional " --max-time <d>" fragment, already
|
|
457
|
+
# shell-quoted; it collapses to nothing when no deadline was derived.
|
|
405
458
|
if [ -n "$model" ]; then
|
|
406
|
-
printf 'capture_omp %q
|
|
407
|
-
"$label" "$model" "$omp_tools" "$skill" "$stdin_file"
|
|
459
|
+
printf 'capture_omp %q --%s --yolo --no-session --no-title --model %q --tools %q -p %q < %q\n' \
|
|
460
|
+
"$label" "$max_time_txt" "$model" "$omp_tools" "$skill" "$stdin_file"
|
|
408
461
|
else
|
|
409
|
-
printf 'capture_omp %q
|
|
410
|
-
"$label" "$omp_tools" "$skill" "$stdin_file"
|
|
462
|
+
printf 'capture_omp %q --%s --yolo --no-session --no-title --tools %q -p %q < %q\n' \
|
|
463
|
+
"$label" "$max_time_txt" "$omp_tools" "$skill" "$stdin_file"
|
|
411
464
|
fi
|
|
412
465
|
} > "$runner"
|
|
413
466
|
chmod +x "$runner"
|
|
@@ -420,9 +473,9 @@ pipe_run_omp() {
|
|
|
420
473
|
pipe_scrub_agent_secrets
|
|
421
474
|
export PIPE_CAPTURE_MODEL="$model"
|
|
422
475
|
if [ -n "$model" ]; then
|
|
423
|
-
capture_omp "$label" -- --yolo --no-session --no-title --model "$model" --tools "$omp_tools" -p "$skill" < "$stdin_file"
|
|
476
|
+
capture_omp "$label" -- "${max_time_args[@]}" --yolo --no-session --no-title --model "$model" --tools "$omp_tools" -p "$skill" < "$stdin_file"
|
|
424
477
|
else
|
|
425
|
-
capture_omp "$label" -- --yolo --no-session --no-title --tools "$omp_tools" -p "$skill" < "$stdin_file"
|
|
478
|
+
capture_omp "$label" -- "${max_time_args[@]}" --yolo --no-session --no-title --tools "$omp_tools" -p "$skill" < "$stdin_file"
|
|
426
479
|
fi
|
|
427
480
|
) || true
|
|
428
481
|
fi
|
|
@@ -186,9 +186,13 @@ MRS_VIEW=$(jq -c \
|
|
|
186
186
|
elif ($body | test($reintroduce; "i")) then "reintroduces"
|
|
187
187
|
else null end
|
|
188
188
|
),
|
|
189
|
+
# match/2 with "g", not scan/2: a Debian bookworm based runner image
|
|
190
|
+
# ships jq 1.6, which has no scan/2 and fails the whole program with
|
|
191
|
+
# a compile error.
|
|
189
192
|
references: [
|
|
190
193
|
$body
|
|
191
|
-
|
|
|
194
|
+
| match("(?:" + $revert + "|" + $regression + "|" + $reintroduce + ")[[:space:]]*[!#]?([0-9]+)"; "gi")
|
|
195
|
+
| .captures[].string
|
|
192
196
|
| select(. != null)
|
|
193
197
|
| tonumber
|
|
194
198
|
]
|