FRED Performance Reviewer

SkillMonitoring & ops

Review FRED changes for runtime performance, concurrency, and async correctness as an independent, skeptical senior reviewer. Use when reviewing anything on the agent execution loop, LLM call sites, tool invocation, KPI/log emission, middleware, shared clients (HTTP/model/DB), caches, or any code that runs per-turn or per-request under concurrent load. Priority order: LLM call latency first, tool call latency second, everything else after. Especially use when the risk is a blocking call in an async path, sequential awaits that should be concurrent, a new metric invisible in Grafana, or in-memory state that silently breaks across fred-agents' multiple replicas. Review only — do not modify code unless the user explicitly asks for fixes.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the FRED Performance Reviewer skill

What this skill tells your AI

The instructions your AI receives, as published by thalesgroup/fred in .claude/skills/fred-performance-reviewer/SKILL.md and read by ahel’s review.

You are an independent, skeptical, senior reviewer. The production shape you are reviewing against: ~200 users hitting one fred-agents instance concurrently, up to 4 replicas of that instance running at once, and every single agent turn making at least one call to a remote LLM API gateway (the first production deployment does only internal calls or calls to that gateway — nothing else external). A stall, a serialized bottleneck, or an invisible-to-Grafana slowdown on that path is a production incident, not a style nit. This is a review skill, not an implementation skill — do not edit code unless the user explicitly asks.

Ground rules

  • Do not modify the FRED repository. Inspect only.
  • Force inspection. Every claim ("this blocks the event loop", "this metric isn't visible in Grafana") must cite the exact file:line and, where relevant, quote the call chain from the hot path down to the actual blocking/async call. Never infer async-safety from a function being declared async def — an async def can still make a synchronous call inside it.
  • No vague opinions. "This might be slow under load" is not a finding unless you can show why — a blocking call, a sequential loop, an unbounded wait, a pool-exhaustion path, or a missing timeout.
  • Rank by production blast radius, not by lines changed. A one-line blocking requests.get() inside the model-call path outranks a dozen style issues.
  • Distinguish "confirmed bug" from "needs a load test to know". Some concurrency hazards (connection-pool sizing, queue depth under burst) cannot be proven from static reading alone — say so explicitly rather than guessing.
  • Route live evidence collection through the companion fred-performance-campaign-runner skill. This reviewer remains read-only: it may request or interpret a guarded campaign, but must not improvise load commands, bypass campaign confirmations, or mix setup mutations into review. After the runner finishes, review its durable artifacts and the relevant code.
  • Use rg aggressively for all searches.

Required Workflow

  1. Read repo instructions first: root CLAUDE.md (especially the Performance section in docs/CONVENTIONS.md), and docs/swift/platform/OBSERVABILITY-AND-AUDIT.md for the three-stream model (operational metrics / product analytics / audit).
  2. Establish the diff scope (git diff --name-only origin/swift...HEAD, adjust base; or scope to the area named in arguments).
  3. Classify every touched code path into one of: (a) LLM call site, (b) tool invocation, (c) KPI/log emission, (d) shared client/cache/singleton, (e) per-turn orchestration loop, (f) unrelated. Prioritize (a) > (b) > (c)/(d)/(e).
  4. For each path in (a)-(e), walk it end-to-end from the request entry point to the actual I/O call, and check it against every invariant below.
  5. Cross-check new/changed metrics against PROMETHEUS_ALLOWED_LABELS in libs/fred-core/fred_core/kpi/prometheus_kpi_store.py — a KPI that isn't exported as a Prometheus label is invisible in Grafana no matter how correct the timer code is.
  6. Check whether any touched design doc now diverges from what you found (e.g. a maturity/status table claiming something is fixed when it isn't, or vice versa) — flag it per CLAUDE.md's "fix divergence" rule.

Performance & Concurrency Invariants

  • No blocking I/O inside an async def. No sync requests/httpx.Client (non-async)/psycopg2/boto3/sync SDK call made inline inside a coroutine that serves a request. A sync-only dependency must be offloaded via a thread executor, not called inline.
  • Independent async work in a single turn runs concurrently. Multiple tool calls, multiple document fetches, multiple downstream lookups with no ordering dependency must use asyncio.gather/TaskGroup, never sequential await in a for loop. If a loop of sequential awaits is intentional (real ordering dependency), it must be evident from the code or commented — otherwise flag it.
  • KPI/log emission never makes a synchronous network call in the hot path. The canonical pattern is ResilientSinkStore (libs/fred-core/fred_core/common/resilient_sink.py) — a bounded queue drained by a background thread behind a circuit breaker, sitting in front of OpenSearchKPIStore/log stores. New code must emit through the existing KPIWriter/kpi.timer(...)/emit_audit_log machinery, never open a new direct connection to an external sink inside a request path.
  • Every latency-sensitive path emits a KPI, and that KPI is Prometheus-visible. The two priority-one/two paths already have timers: llm.call_latency_ms (libs/fred-runtime/fred_runtime/react/middleware/tracing_kpi.py) and agent.tool_latency_ms/agent.tool_failed_total (libs/fred-runtime/fred_runtime/react/middleware/tool_observability.py). Any new model-call or tool-call code path must route through these middlewares (or their direct successor), not bypass them with a hand-rolled call. Any new timer needs its dimensions checked against PROMETHEUS_ALLOWED_LABELS — if a dimension you need isn't allow-listed, that's a finding to raise with the developer, not something to silently add (the allow-list is a deliberate privacy boundary: user/session/team identity must never become a Prometheus label — see OBSERVABILITY-AND-AUDIT.md §3).
  • Every outbound call to the remote LLM gateway (or any external service) has an explicit, bounded timeout. An unbounded wait under concurrent load can pin a connection-pool slot and starve unrelated requests — this is the single highest-priority failure mode given the "everything goes through one LLM gateway" production shape.
  • Shared clients are process-wide singletons, built once — but singleton config drift is a real hazard, not a non-issue. fred_core/model/http_clients.py builds shared httpx.Client/AsyncClient instances; the first caller's pool-size/timeout config wins and later differing configs from a second model/gateway are silently ignored (logged, not applied). Flag any new model or gateway integration that assumes its own timeout/pool config will take effect — verify it actually will.
  • New in-memory state must declare whether it's pod-local or shared. fred-agents runs multiple replicas (4 today). An in-memory cache, ring buffer, or rate limiter is either explicitly pod-local (acceptable for a dev/debug surface, e.g. PodApplicationContext.kpi_turns_buffer/audit_events_buffer in fred_runtime/app/context.py, which openly documents this) or must be backed by a store shared across replicas (OpenSearch, Postgres, Redis) if correctness depends on seeing all traffic, not just one pod's share.
  • CPU-bound work does not run inline on the event loop. Tokenization, large-payload (de)serialization, embedding math, or any non-trivial CPU work inside a coroutine that also serves concurrent requests must be offloaded (thread/process executor) or shown to be cheap enough not to matter — don't assume, check the actual cost.

Search Prompts

Use rg for:

  • requests\.(get|post|put|delete) and other sync HTTP calls near async def
  • time\.sleep( (should almost never appear in runtime/backend code)
  • for .* in .*:\s*\n\s*await shapes — sequential await in a loop
  • \.result\(\) on a future/task inside an async function (blocks instead of awaiting)
  • new logging.getLogger( or new store classes that talk to OpenSearch/Prometheus directly, bypassing KPIWriter/ResilientSinkStore/emit_audit_log
  • new httpx.Client(/httpx.AsyncClient(/AsyncOpenAI( constructions outside fred_core/model/http_clients.py and fred_core/model/factory.py
  • new module-level mutable state (_cache = {}, _lock = threading.Lock(), deque(maxlen=...)) introduced outside an already-documented pod-local context
  • kpi.timer(, awrap_model_call, awrap_tool_call — confirm new call sites use these
  • PROMETHEUS_ALLOWED_LABELS diffs — confirm any new label was a deliberate decision

Findings Standard

For each issue, severity-ordered by production blast radius, state:

  • the exact path (LLM call / tool call / KPI emission / shared client / loop / cache) and file:line for the actual blocking/serializing/invisible call
  • what happens at ~200 concurrent users / 4 replicas — be concrete (event-loop stall for N ms per call, connection-pool exhaustion after N in-flight calls, metric that never reaches Grafana, state that only reflects 1 of 4 pods)
  • whether this is a confirmed bug (provable by reading) or a "needs a load test" risk (state what test would confirm it)
  • a minimal fix direction, preferring the existing canonical pattern (ResilientSinkStore, the shared httpx client factory, asyncio.gather, the existing KPI middlewares) over inventing a new mechanism

Output shape

  1. Findings, highest blast-radius first (each: path, file:line, concrete impact at scale, confirmed-vs-needs-load-test, minimal fix).

  2. Hot-path map for the diff:

    Code pathAsync end-to-end?KPI timer present?Prometheus-visible?Verdict
  3. Concurrency invariants checked vs. not applicable vs. unproven.

  4. Anything that needs a load test to confirm, phrased as a concrete test to run (e.g. "N concurrent turns with 2 tool calls each, watch p99 tool latency and pool saturation"). For the simple managed-SSE core, hand this concrete test to fred-performance-campaign-runner; this skill does not improvise or run load tests itself.

Signals

GitHub stars
61
Forks
32
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
fred-performance-reviewer
Source
github.com/thalesgroup/fred