- ClaudeConfig gains an oauth: block (custom endpoints, e.g. a private
gateway); without it, auth: oauth keeps the stock Claude Pro/Max provider
- Claude-Code system-prefix spoof + anthropic-beta header are only sent on
the stock provider path
- OAuth tokens are stamped with an issuer fingerprint (token_url|client_id)
so switching endpoints never replays a token against the wrong issuer
- All auth flows refresh the in-memory access-token cache on save, so
.authenticate mid-session takes effect immediately
- A custom oauth: block now requires api_base: refusing to silently fall
back to api.anthropic.com prevents sending a gateway token to the wrong
host
SystemTime nanos alone collide on coarse clocks, so concurrent tests shared
a scratch dir and read each other's generated sbx mixins (flaky
generated_sbx_mixin_* failures in CI).
verdict_gate.py now names every incomplete review on a machine line the
done template renders immediately after the verdict:
`GAUNTLET_REVIEW_INCOMPLETE: <name>[, <name>]` (sorted). Names are the
lanes retry_gate gave up on (`review_incomplete`) plus the `pipeline`
pseudo-lane whenever `signals_error` carries a `PIPELINE-FAULT:` —
parse death, builder / retry-gate / default-lanes crash guards — and the
gate's own crash guard emits the `pipeline` line too. The report gains a
`## Review incomplete` section (after the signals_error blockquote,
immediately before `## Full lane reports`) and the three PIPELINE-FAULT
lane rows carry ` ×N` when the lane was re-run (N = 1 texts unchanged).
retry_gate.py stands in a synthetic `PIPELINE-FAULT: <lane> lane produced
no report` marker for a lane that ran but collected nothing, so it is
stashed, classified FAULTED and never renders SKIPPED downstream.
MAX_RETRY_ELAPSED_SECS trimmed 45600 → 45000 for margin slack.
graph.yaml: `gauntlet_incomplete_line` seeded in initial_state and
spliced into the done output. README + mermaid document retry_gate, the
dotted _next back-edge, the 3-attempt / wall-clock budget, that retry
never narrows selection, the crash-guard append semantics, and the
machine-line contract (present whenever any lane or the pipeline was
incomplete; it can accompany real findings).
Tests: ×N on all three branches (N>1, N=1 byte-identical, no leak onto
non-fault rows), exact incomplete line / section placement, pipeline
pseudo-lane alone and alongside real lanes (via default_lanes.py's real
crash guard), mixed finding+fault, crash-guard chaining from
build_items / retry_gate / parse_fault / verdict_gate, synthetic
no-report marker, done-template wiring, README pins (positive + stale
negative).
Give the review gauntlet a deterministic retry loop for lanes that FAULT
(PIPELINE-FAULT marker, missing verdict sentinel, or a nested graph's own
degraded-run wording) without ever re-running a lane that reached a real
verdict.
- graph.yaml: the four lane maps route to a new `retry_gate` script node
(static `next: verdict_gate`; emits `_next: build_items` to retry).
initial_state gains lane_attempts / kept_results / retry_lanes /
review_incomplete / gauntlet_started_at. settings.timeout 24000 -> 69600
(3 passes x 2 attempts x 11400 + 1200 margin).
- scripts/retry_gate.py (new): stashes every lane report that ran
(completed and faulted alike) and restores reports a re-run pass
overwrote with []; classifies with verdict_gate.py's sentinel regexes
verbatim; counts attempts; retries while attempts < 3 and inside a
45600 s wall-clock budget; otherwise records the lane in
review_incomplete and falls through. Crash guard appends to
signals_error and never retries.
- scripts/signals.py: stamps gauntlet_started_at before the git work so a
failed diff still starts the clock.
- scripts/build_items.py: retry pass emits items only for retry_lanes
(with attempt numbers), adds a `retried:` note to lanes_summary, resets
retry_lanes; falsy-guarded stamp fallback; crash guard appends to
signals_error and preserves lanes_summary.
- Tests: hermetic python3-guarded script tests for retry_gate /
build_items / signals, graph wiring + initial_state pins, sentinel and
LANE_KEYS cross-pins, retry-budget envelope vs settings.timeout, a
first-pass golden payload, and an engine-level executor test proving a
script `_next` back-edge re-fires a static fan-out and an empty map
re-run clears its collect_into.
Plan: PLAN-review-retry section A items 1-3 and 5. Verdict/report surface
(section A items 4 and 6) is TASK-032.
Child escalations (coder plan approvals, deviation gates, user__* calls
from subagents) previously expired after the escalating agent's
escalation_timeout, defaulting to 300 s, and 0 timed out instantly. In
autonomous runs the orchestrator often could not reply within that
window, so approvals silently fell through to the best-judgment
fallback.
- Extract await_escalation_reply as the wait seam: 0 awaits the oneshot
with no deadline; nonzero keeps tokio::time::timeout with the same
timeout message. A dropped sender still resolves to the existing
cancelled reply either way.
- DEFAULT_ESCALATION_TIMEOUT_SECS and default_escalation_timeout() -> 0,
so graph agents (which inherit the default via from_graph) are covered.
- Document the new semantics in config.agent.example.yaml and drop the
5-minute-timeout claim from the default spawn instructions.
- Tests: paused-clock unlimited wait (3600 s), nonzero expiry message,
dropped-sender completion, default pin, and a grep pin that the stale
timeout wording is gone from src/, assets/, and the example config.
Plan: PLAN-review-retry §C (D3).