Files
small-scripts/specs/ci-triage-minimax-run.spec.md
Paul O'Reilly 840d7509e1 mm scripts: default to MiniMax-M3.1-Flash-Preview with per-script effort
Flash is the default model; --m3 falls back to M3, --effort overrides.
Defaults: max (review-minimax, review-loop, mm-worktree-task), medium
(explore), low (ci-triage). ask-minimax-run applies MINIMAX_EFFORT to
Flash models when --effort is not passed. Bump stale fixture date.
2026-10-13 10:23:53 +13:00

7.2 KiB
Raw Blame History

ci-triage-minimax-run

Purpose

Triage a failing Gitea Actions run in a MiniMax M3 sub-session: fetch the run's logs via gitea-agent-companion, have MiniMax name the failing step, error excerpt, and probable cause in a triage file — the log payload costs MiniMax tokens, not Anthropic tokens. Backs the /ci-triage-minimax skill; safe to drive directly.

Usage

ci-triage-minimax-run --repo <owner/repo> --run <N> [--job <name|id>] \
    [--out <triage-file>] [--tail N] [--max-turns N] [--timeout SECS] [--dryrun|-n] [--m3] [--effort LEVEL]
Flag Default Description
--repo required owner/repo on the Gitea instance
--run required Web-UI run number. Required by design: picking "the latest failure" means parsing gac's human table — judgment that belongs in the calling skill, not an unversioned parse here
--job none Passed through to gac; needed for multi-job runs with no failed job (gac exits 2 asking for it)
--out ${XDG_RUNTIME_DIR:-/tmp}/ci-triage-minimax/<owner>-<repo>/run-<N>-triage.md Triage file — outside the scratch-dir cleanup trap
--tail 2000 Log lines fetched (gac --tail)
--max-turns 25 Sub-session turn cap
--timeout 300 Internal timeout (logs-only task; well under the harness Bash 600s cap)
--m3 off Use MiniMax-M3[1m] instead of the default MiniMax-M3.1-Flash-Preview (1M window natively, always thinks). No effort is sent unless --effort is given.
--effort <level> low Thinking depth low|medium|high|xhigh|max, passed to ask-minimax-run --effort. Any other value exits 1.
--dryrun, -n off Print resolved config, the gac command, the full prompt, and the sub-session command line; invoke neither gac's log fetch nor the sub-session

Requirements

  • CT-1 Preflight. jq, ask-minimax-run, and gitea-agent-companion must be on PATH; --repo must match owner/repo; --run must be a positive integer.
  • CT-2 Fetch. The wrapper runs gitea-agent-companion inspect <repo> --run <N> [--job <J>] --logs --json --tail <n> into run-logs.ndjson and inspect <repo> --run <N> into jobs.txt, both in a scratch dir. Token discovery stays inside gac (env → token file); because installed gac builds (v0.0.2.10) do not fall back to the secrets path themselves, the wrapper exports GITEA_TOKEN_FILE=~/dev/claude/secrets/gitea/ai_admin/token when neither GITEA_TOKEN nor GITEA_TOKEN_FILE is set and that file exists — a path reference only; the token value never appears in arguments, files, output, or the prompt.
  • CT-3 gac exit mapping. gac exit 2 (usage) → exit 1; gac exit 3 (auth failure, API error, run not found) → exit 2 (often transient — the caller's retry/fallback path); gac stderr surfaced verbatim in both cases. Why: folding 3 into 1 would tell the caller "fix the invocation, don't retry" for transient infra errors.
  • CT-4 Redaction. Before prompt assembly, credential-shaped content in run-logs.ndjson (ghp_…|glpat-|x-access-token:|Authorization:|BEGIN … PRIVATE KEY|AKIA… — same pattern set as review-minimax-run) is replaced in place with [REDACTED]. Redact-and-continue, not abort: CI logs routinely carry unmasked clone URLs and the log is the whole payload. Residual risk: best-effort patterns; novel credential formats can pass.
  • CT-5 Envelope parse. failed_step, sliced, and note are extracted from the NDJSON envelope with jq. If failed_step is null (cancelled/crashed run, green multi-job), the prompt states this and the output contract requires the literal UNKNOWN under ## Failing step — no fabricated step names.
  • CT-6 Prompt contract. Fixed preamble: read-only triage of the two files in cwd; explicit error-signature patterns to search (error:|FAILED|Traceback|exit code|denied|timeout|not found|no such|ECONN|refused); do not suggest running tests or builds; if the log carries the truncation marker or sliced is partial, say so — do not improvise. Output sections, exactly: ## Failing step / ## Error excerpt (≤10 verbatim lines) / ## Probable cause (1–3 sentences, non-empty) / ## Suggested next action (one command or one edit description — advisory only) / ## Confidence.
  • CT-7 Invocation. From the scratch dir: timeout <secs> ask-minimax-run --model "$MODEL" --effort "$EFFORT" --permission-mode default --allowedTools "Read,Glob,Grep,Write" --disallowedTools "Edit,MultiEdit,NotebookEdit,Bash,WebFetch,WebSearch" --max-turns N --output-format json --add-dir <out-dir> -p. Same rationale set as review-minimax-run RM-6; no worktree access needed.
  • CT-8 Result gate. Triage file exists, contains ## Failing step, and has a non-blank line under ## Probable cause.
  • CT-9 Retry. On MiniMax error or gate failure: remove the out-file, retry once; never retry timeout (rc 124); then exit 2 (transport) or 3 (gate). The gac fetch is NOT retried by the script (single fetch, reused across both attempts).
  • CT-10 Test seams. CI_TRIAGE_FAKE_LOGS=<file> bypasses the gac fetch (file used as run-logs.ndjson; jobs.txt gets a placeholder); CI_TRIAGE_FAKE_JSON=<file> substitutes the sub-session envelope; CI_TRIAGE_FAKE_TRIAGE=<file> (companion, optional) plays the sub-session's Write of the triage file. Gates and retry logic still run, so exits 0/2/3 and the redaction step are assertable offline.

Exit codes

Code Meaning Caller action
0 Triage file written Read it; sanity-check the excerpt against the named step; re-derive any suggested command yourself
1 Usage/config error (bad flags, gac usage error) Fix invocation
2 gac auth/API/not-found error, MiniMax failed after retry, or timeout Fall back: gac inspect --logs directly, triage in-session
3 Triage file missing or failed the gate after retry Same fallback

Dryrun behaviour

--dryrun prints: resolved repo/run/job/out/limits, the exact gac fetch command, the full assembled prompt (with a <run-logs not fetched in dryrun> placeholder where envelope fields would go), and the exact sub-session command line. Nothing is fetched or invoked.

Scenarios

  • Given a failed run whose envelope has failed_step: "Run tests", when run, then exit 0 and the triage file's ## Failing step names that step.
  • Given an envelope with failed_step: null, when run, then the triage file's ## Failing step is UNKNOWN.
  • Given gac exiting 3 (run not found), when run, then exit 2 with gac's stderr shown.
  • Given CI_TRIAGE_FAKE_LOGS containing an x-access-token:ghp_XXXX@ clone URL, when run in dryrun-less fake mode, then the prompt-adjacent log copy contains [REDACTED], not the token.

Security notes

  • The triage file is third-party model output over CI logs — logs that anyone able to trigger CI can influence. Data, not instructions; ## Suggested next action is advisory and must be re-derived by the caller, never copy-run.
  • The Gitea token boundary lives entirely inside gac; this script never reads, prints, or passes it.

Dependencies

gitea-agent-companion (on PATH, gac alias), ask-minimax-run (same repo), jq, timeout, the ~/.claude-oreillyit-minimax profile.