Skip to content

[gh-aw-threat-detection] pipe --engine-timeout, --max-turns, --retries from workflow frontmatter #55530

Description

@davidslater

Context

github/gh-aw-threat-detection v0.4.12 (PR #918) adds three per-attempt kill switches to the threat-detect binary that we invoke from compiled detection jobs:

  • --engine-timeout — per-attempt wall-clock timeout (detector default: 5m). On expiry, the engine subprocess and every descendant it spawned are killed with a process-group SIGKILL. Terminal — never retried, because a runaway model is overwhelmingly likely to run away again.
  • --max-turns — per-attempt agentic turn cap (detector default: 50). Exported to the engine as GH_AW_MAX_TURNS and additionally passed as --max-turns to Claude's bare CLI.
  • --retries — from-scratch retries after a "clean exit without verdict" (detector default: 0). Timeouts and engine crashes are terminal regardless.

Each of these is also readable from an env var: THREAT_DETECTION_ENGINE_TIMEOUT, THREAT_DETECTION_MAX_TURNS (with GH_AW_MAX_TURNS as a fallback), THREAT_DETECTION_RETRIES.

Today the compiled .lock.yml invocation is just:

- name: Run threat detection
  run: |
    threat-detect --engine ${{ ... }} --output /tmp/gh-aw/threat-detection/detection_result.json /tmp/gh-aw/threat-detection

so a workflow author can't override any of these three caps per workflow. The detector's own defaults are already sane, but two use cases need per-workflow tuning:

  1. Heavy prompts / lots of files. A workflow that legitimately expects the detector to inspect many patches or a very large agent_output.json may need --engine-timeout 10m or --max-turns 100.
  2. Fast, cheap workflows. A trigger-happy workflow that runs on every PR comment may want to tighten to --engine-timeout 90s to cap credit spend on a burst.

Note: GH_AW_MAX_TURNS is already piped through — the standalone detector reads it as a fallback for --max-turns. So a workflow that sets engine.max-turns in frontmatter for its main agent already applies the same cap to the detector today, no compiler change needed. This issue is about the two flags that aren't covered (--engine-timeout, --retries) plus a detector-specific override for --max-turns that doesn't couple it to the main agent's cap.

Proposal

Add a threat-detection (or safe-outputs.threat-detection, wherever the existing knobs live) frontmatter section:

threat-detection:
  engine-timeout: 10m       # Go duration; 0 disables
  max-turns: 100            # Detector-only override; when unset falls back to top-level engine.max-turns / GH_AW_MAX_TURNS, then to the detector's own default (currently 50)
  retries: 1

Compiler behavior:

  • Only emit each flag when the user set it in frontmatter. When a key is unset in the workflow's frontmatter, the compiler MUST NOT emit the corresponding flag (or an env var) on the shell line. The detector's own compiled-in default (5m / 50 / 0) then applies. This is the key rule: if the compiler hardcoded the detector's current default onto every emitted line, bumping that default upstream (as we just did going from 20 → 50 on --max-turns) would require every workflow to recompile before picking it up. Emitting nothing on unset keeps the coupling one-way.
  • Prefer flags over env vars in the emitted script so the invocation is self-describing.
  • Validate at compile time: reject negative durations/ints (the detector already rejects them with config_error, but catching it at compile time is a better UX).

Resulting invocation for a workflow that sets all three:

- name: Run threat detection
  run: |
    threat-detect \
      --engine ${{ ... }} \
      --engine-timeout 10m \
      --max-turns 100 \
      --retries 1 \
      --output /tmp/gh-aw/threat-detection/detection_result.json \
      /tmp/gh-aw/threat-detection

Resulting invocation for a workflow that sets none of them (unchanged from today):

- name: Run threat detection
  run: |
    threat-detect --engine ${{ ... }} --output /tmp/gh-aw/threat-detection/detection_result.json /tmp/gh-aw/threat-detection

Compatibility

  • Backward compatible: workflows that don't set the new keys keep the current behavior (detector defaults apply, invocation is byte-identical to today's).
  • No coordinated release needed — the detector already accepts all three flags. This is a pure code-generation change in gh-aw.
  • No .lock.yml migration needed for workflows that don't opt in.
  • Future detector-default changes propagate automatically to workflows that haven't opted into an explicit override.

Related

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions