Skip to content

fix: eliminate proxy-fanout signal that triggers free_mode_run_fanout refusal - #207

Merged
trefeon merged 1 commit into
trefeon:mainfrom
ItszFinn:fix/run-fanout-free-mode
Aug 25, 2026
Merged

fix: eliminate proxy-fanout signal that triggers free_mode_run_fanout refusal#207
trefeon merged 1 commit into
trefeon:mainfrom
ItszFinn:fix/run-fanout-free-mode

Conversation

@ItszFinn

Copy link
Copy Markdown
Contributor

Summary

This PR fixes the free_mode_run_fanout refusal that was killing client turns with a dead 502 upstream_unavailable. The root cause was that the proxy was producing the exact "proxy fanout" shape upstream classifies as ban-grade evidence (freebuff-spend-ceilings.ts).

Changes

client_id per run (not per call)

The CLI mints client_id once per prompt (run.ts:722/822) and reuses it on every LLM step. The proxy was drawing a fresh client_id per chat call, making one run_id fan out across N client_ids — the exact anti-pattern upstream refuses. Now ClientID is minted once by the run manager and threaded through the envelope.

Dropped prewarm fleet

Pool.Start no longer fans out a run per registry agent per token at boot. That fleet held ~one concurrent agent run per served model on a single free account — the "proxy fanout" sweep signal. Runs now START lazily on first Acquire.

free_mode_run_fanout → 429 (was 502)

The upstream refusal body {"error":"free_mode_run_fanout","message":"Free mode request rejected."} was falling through to the generic UpstreamError branch → dead 502. Now classified as RateLimitError with bounded FanoutCooldown (60s) and surfaced as 429 so clients back off. Never re-POSTs into a fanout refusal.

Luna retired root override

openai/gpt-5.6-luna's root agent was retired upstream (free_mode_legacy_luna_agent). Added retiredRootOverrides map: base2-free-lunabase3-free-luna.

ScarceSessionError accuracy

Now reports the HELD session's model (not the requested one) for correct Retry-After hints.

Run persistence fix

ClientID is persisted with the run so a restart doesn't split a run's identity.

Build tags

Makefile and Taskfile use -tags dashboard for Go builds.

Tests

All 24 test suites pass. New tests:

  • TestChatRunFanoutSurfaced429 — end-to-end: fanout body → 429 + Retry-After, one chat call only
  • TestChatClientIDStableAcrossRun — same client_id across calls on one run
  • TestInjectEnvelopeClientIDPerRun — envelope repeats the run's client_id
  • TestClassifyRunFanout — fanout body classified as RateLimitError on any status
  • TestRetiredRootOverrideAppliesToBothPaths — offline and live-parse paths agree
  • TestStartDoesNotFanOutAndShutdownDrains — no boot fleet, drains one lease

… refusal

- Make client_id per-RUN instead of per-call: the CLI mints it once per prompt
  (run.ts:722/822) and repeats it on every LLM step. N client_ids under one
  run_id was the exact fanout shape upstream refuses as free_mode_run_fanout.
- Drop Start prewarm fleet: no longer fans out a run per agent per token at
  boot — that was the ban-grade proxy-fanout sweep signal.
- Classify free_mode_run_fanout as RateLimitError (not dead upstream 502):
  bounded FanoutCooldown (60s) + 429 surfacing so clients back off instead of
  dying mid-turn.  Never re-POST into a fanout refusal.
- Override luna retired root: base2-free-luna → base3-free-luna for the
  retired upstream agent (free_mode_legacy_luna_agent).
- ScarceSessionError now reports the HELD session's model for accurate
  Retry-After.
- Persist ClientID on runs so resume doesn't split a run's client identity.
- Build tags: Makefile and Taskfile use -tags dashboard.
- Audit: 24 tests including TestChatRunFanoutSurfaced429, TestChatClientID-
  StableAcrossRun, TestInjectEnvelopeClientIDPerRun, TestRetiredRootOverride-
  AppliesToBothPaths, TestStartDoesNotFanOutAndShutdownDrains.

@trefeon trefeon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally on pr-207: go build -tags dashboard clean, gofmt clean, full hermetic suite passes (env -u AUTH_TOKENS -u ADMIN_TOKEN go test ./...). The per-run client_id threads through both pooled and bridge paths via the shared chatAttempt; the fresh-draw fallback covers runless callers (admin smoke ping). Merging.

@trefeon
trefeon merged commit eb4801a into trefeon:main Aug 25, 2026
6 checks passed
@trefeon trefeon mentioned this pull request Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants