Skip to content

fix: contain upstream failures in gateful and keep RPC out of the proposals request path - #2148

Merged
brunod-e merged 17 commits into
devfrom
brunod-e/proposals-resilience
Sep 15, 2026
Merged

brunod-e merged 17 commits into
devfrom
brunod-e/proposals-resilience

Conversation

@brunod-e

@brunod-e brunod-e commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Contexto

Investigação da DEV-1163 sobre as quedas de julho (25-27/07), 19/08 e 26-28/08. Em todos os casos o encadeamento foi o mesmo: um punhado de falhas reais (RPC com blip, Postgres lento, rota com dependência externa quebrada) -> circuit breaker por DAO abre com 5 falhas consecutivas -> todas as rotas do DAO respondem 503 por 5 a 40 minutos, com o probe de recuperação caindo justamente na rota quebrada. Em 26/08 17-19h, 63 falhas reais viraram 722 x 503 no ENS; o FLUID ficou 13h fora com a API saudável.

Este PR reduz o raio da explosão. Não corrige as causas (essas estão no PR da DEV-1163).

Mudanças

gateful

  • Circuit breaker abre por taxa de falha em janela deslizante (30s, mínimo 10 requests, 50%) em vez de 5 falhas consecutivas: um reload que falha parcialmente não abre mais o circuito; uma queda real ainda abre em segundos.
  • Breaker por DAO + rota (ens:proposals, ens:revenue...): uma rota quebrada não derruba as demais do DAO. /health reporta o pior estado entre as rotas do DAO.
  • Cooldown inicial 5min -> 30s (máximo 40min -> 5min): recuperação acompanha o upstream.
  • Cache Redis mantém entradas por 5min além do max-age e serve stale quando o upstream falha (5xx, timeout, circuito aberto) em vez de erro.
  • Env vars: CIRCUIT_BREAKER_WINDOW_MS, CIRCUIT_BREAKER_MIN_REQUESTS, CIRCUIT_BREAKER_FAILURE_RATE substituem CIRCUIT_BREAKER_FAILURE_THRESHOLD (atualizar na Railway se estiver definida).

api

  • Latest block servido stale-while-revalidate: request nunca espera o RPC depois do warm-up; falha de RPC faz backoff de 3s e mantém o bloco anterior.
  • Quando o RPC está fora, /proposals, /proposals/{id} e /proposals-activity respondem com o status indexado em vez de falhar (incidente de 19/08: blip de 20min do eRPC derrubou todos os DAOs por >1h).

Verificação

  • pnpm api typecheck / pnpm gateful typecheck: OK
  • pnpm api lint: OK (gateful via lint-staged)
  • API: 104 arquivos, 1085 testes; gateful: 129 testes

🤖 Generated with Claude Code

brunod-e and others added 4 commits September 2, 2026 15:15
Serve the latest block stale-while-revalidate so requests never wait on
eth_getBlockByNumber once the cache is warm, back off after a failed
refresh, and degrade to indexed proposal statuses when the RPC is down
instead of failing the request.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replace the consecutive-failure counter with a sliding-window failure
rate that needs a minimum sample before opening, so a partially failing
reload burst no longer takes a DAO offline while a real outage still
trips within seconds. Proxy breakers are now keyed per DAO and route
group so one slow route cannot block the others, and the default
cooldown drops from 5min to 30s (max 5min) so recovery follows the
upstream instead of lagging it by up to 40min. Health reports the worst
per-route state for each DAO.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Keep Redis entries for a 5min grace period past their max-age and, when
revalidation hits a 5xx, a timeout or an open circuit, answer with the
stale body and Cache-Status: Redis; stale instead of an error.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
anticapture-storybook Ready Ready Preview Sep 14, 2026 10:07pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
anticapture Ignored Ignored Sep 14, 2026 10:07pm UTC

Request Review

@railway-app

railway-app Bot commented Sep 3, 2026

Copy link
Copy Markdown

🚅 Deployed to the anticapture-pr-2148 environment in anticapture-infra

Service Status Web Updated
obol-api ✅ Success (View Logs) Sep 14, 2026 at 10:41 pm UTC
gateful ✅ Success (View Logs) Web Sep 14, 2026 at 10:06 pm UTC
loki ✅ Success (View Logs) Sep 14, 2026 at 10:06 pm UTC
grafana ✅ Success (View Logs) Web Sep 14, 2026 at 10:06 pm UTC
authful ✅ Success (View Logs) Web Sep 14, 2026 at 10:06 pm UTC
prometheus ✅ Success (View Logs) Sep 14, 2026 at 10:06 pm UTC
tempo ✅ Success (View Logs) Sep 14, 2026 at 10:05 pm UTC
alertmanager ✅ Success (View Logs) Web Sep 14, 2026 at 10:05 pm UTC
otelcol ✅ Success (View Logs) Sep 14, 2026 at 10:05 pm UTC
docs ✅ Success (View Logs) Web Sep 14, 2026 at 10:05 pm UTC
mcp ✅ Success (View Logs) Web Sep 14, 2026 at 10:05 pm UTC
nouns-api ✅ Success (View Logs) Sep 14, 2026 at 9:54 pm UTC
compound-api ✅ Success (View Logs) Sep 14, 2026 at 9:54 pm UTC
scroll-api ✅ Success (View Logs) Sep 14, 2026 at 9:54 pm UTC
ens-api ✅ Success (View Logs) Sep 14, 2026 at 9:53 pm UTC
lil-nouns-api ✅ Success (View Logs) Sep 14, 2026 at 9:53 pm UTC
tornado-api ✅ Success (View Logs) Sep 14, 2026 at 9:53 pm UTC
shutter-api ✅ Success (View Logs) Sep 14, 2026 at 9:53 pm UTC
fluid-api ✅ Success (View Logs) Sep 14, 2026 at 9:53 pm UTC
gitcoin-api ✅ Success (View Logs) Sep 14, 2026 at 9:53 pm UTC
uniswap-api ✅ Success (View Logs) Sep 14, 2026 at 9:52 pm UTC
aave-api ✅ Success (View Logs) Sep 14, 2026 at 9:52 pm UTC
uniswap-indexer-offchain ✅ Success (View Logs) Sep 3, 2026 at 6:14 pm UTC
ens-indexer-offchain ✅ Success (View Logs) Sep 3, 2026 at 6:14 pm UTC
compound-indexer-offchain ✅ Success (View Logs) Sep 3, 2026 at 6:11 pm UTC
shutter-indexer-offchain ✅ Success (View Logs) Sep 3, 2026 at 6:09 pm UTC
gitcoin-indexer-offchain ✅ Success (View Logs) Sep 3, 2026 at 6:09 pm UTC
aave-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
fluid-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
compound-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
lil-nouns-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
shutter-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
tornado-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
address-enrichment ✅ Success (View Logs) Web Sep 3, 2026 at 12:22 pm UTC
gitcoin-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
uniswap-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
scroll-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
ens-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
obol-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
nouns-indexer ✅ Success (View Logs) Sep 3, 2026 at 12:22 pm UTC
ens-relayer ✅ Success (View Logs) Sep 3, 2026 at 12:21 pm UTC
erpc ✅ Success (View Logs) Sep 3, 2026 at 12:21 pm UTC
nodeful ✅ Success (View Logs) Sep 3, 2026 at 12:21 pm UTC
user-api ✅ Success (View Logs) Web Sep 3, 2026 at 12:20 pm UTC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82179d9e76

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts Outdated
Comment thread apps/gateful/src/health/route.ts
Comment thread apps/api/src/services/proposals/onchainProposals.ts
Comment thread apps/gateful/src/middlewares/cache.ts Outdated
Comment thread apps/gateful/src/shared/circuit-breaker.ts Outdated
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🔍 Vercel preview: https://anticapture-4s7efbbia-ful.vercel.app

…lth, no-cache on stale

Review feedback on the per-route breakers: route keys derive from client
controlled paths, so the proxy now falls back to the DAO breaker once the
registry holds 128 entries; an OPEN circuit whose cooldown has elapsed no
longer makes /health report the DAO as down (it only transitions on its next
real request, which idle routes may never see); and stale cache responses are
served with Cache-Control: no-cache so browsers do not pin an expired body
for another full max-age.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c36a332432

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/api/src/services/proposals/onchainProposals.ts Outdated
Comment thread apps/api/src/services/proposals-activity/index.ts Outdated
Comment thread apps/gateful/src/shared/circuit-breaker.ts Outdated
Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts Outdated
…ups, guard per-proposal status

Review feedback: the failure ratio is now checked after successes too, so
settlement order under concurrency cannot leave an over-threshold window
closed; per-route breaker keys are limited to the API's known route groups
(unknown segments share the DAO breaker) instead of a global cap a client
could exhaust; and one proposal whose status reads fail (TORN reads quorum
and timelock without a fallback) keeps its indexed status instead of failing
the whole listing or activity request.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10990894a2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts Outdated
Comment thread apps/api/src/clients/governor.base.ts
Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts Outdated
Comment thread apps/gateful/src/shared/circuit-breaker.ts Outdated
Comment thread apps/gateful/src/shared/circuit-breaker.ts Outdated
Comment thread apps/api/src/clients/governor.base.ts
…bound block staleness

Addresses the review on the resilience PR.

gateful
- The breaker had one trip rule (failure rate over a window with at least
  10 requests), so keys that never see 10 requests in 30s (relayer,
  address-enrichment, fan-out) could no longer open at all: a dead relayer
  made every vote wait the full 30s proxy timeout. Quiet keys now open after
  CIRCUIT_BREAKER_CONSECUTIVE_FAILURES (default 5) straight failures; busy
  keys keep the rate rule, so a partially failing reload still does not trip.
- Route breakers are derived from the request path instead of a hardcoded
  list of API routes: any route-shaped first segment gets its own key the
  first time it is hit, ids/hashes/addresses stay on the DAO key, and the
  number of route breakers per DAO is capped at 64 so client-controlled
  paths cannot grow the registry or the metric series without bound.
- Fan-out and the per-DAO health probe use the same per-route breaker as
  the proxy (`ens:dao`, `ens:health`), so an open route short-circuits them
  too instead of them reading a bare DAO key nobody writes to any more.
- Window outcomes are counted in 10 fixed time buckets (Map keyed by bucket
  index) rather than an unbounded array of timestamps.

api
- The stale-while-revalidate latest block is bounded to 60s of age. Past
  that the call waits for the RPC again and a failure surfaces, so proposal
  services fall back to indexed statuses (a proposal whose endBlock passed
  no longer reads ACTIVE for hours) and /health reports chain.head as null.
  Inside the retry backoff of a failed refresh the call fails fast instead
  of re-probing the RPC.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Evicting idle route breakers made the per-DAO cap stop bounding metric
cardinality: every made-up path that took a slot recorded a new
circuit_breaker_state series, and the OTel SDK keeps every attribute set for
the life of the process. Route breakers now record their state lazily, from the
first transition out of CLOSED onwards, so a series exists only for routes that
actually tripped. DAO, relayer and service keys come from configuration and
keep recording eagerly, so dashboards still list them while healthy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0a395fdee

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts Outdated
Two ways a breaker could lose the failures it had counted. A batch of slow
calls kept recording after one of them opened the circuit, pushing the cooldown
forward or reopening a circuit that a probe had already closed; every state
transition now starts a new generation and a call whose generation has moved on
is not counted. Eviction also treated any closed breaker as idle, so made-up
paths interleaved with a failing route could drop that route before it tripped;
only breakers with no live failures and no failure streak are evicted now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d758f0a80f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts Outdated
A breaker whose slow request had not settled yet still looked idle, so another
route could take its slot; the timeout then landed on a detached instance while
the next request for that path got a clean breaker, and slow failures never
added up to a trip. Execution now counts in-flight calls in a finally block on
every state path, and a breaker with anything in flight is not idle. Every call
site passes an abort timeout, so a slot cannot be pinned indefinitely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
brunod-e and others added 4 commits September 14, 2026 18:25
…ery path shape

A route breaker kept its slot for good once it had failed: a streak only ended
on a success and an open circuit only moved on traffic, so one failing call to
each of 64 made-up segments pinned a DAO's slots through an outage and left the
real routes sharing the DAO breaker afterwards. A streak now ends when the
window empties, and a breaker with nothing in flight, no traffic for a window
and no cooldown left is reclaimable. Path keys are also decoded and lowercased
before matching, with every non-route shape on one key per DAO, and the proxy
drops the DAO segment by position so a percent-encoded DAO cannot slip through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…breakers

The per-DAO health probe used a route key, so probe failures reached gateway
readiness through the DAO summary, the back door the probe was written to
avoid; it now has its own key outside the DAO namespace. Fan-out aborts six
times sooner than the proxy and no longer shares the proxy's key, so its
deadline cannot open a circuit for calls that had far more budget. The circuit
breaker windows, counts and cooldowns are now rejected at boot when not above
zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… a failing RPC

The timelock delay and grace period are read only for queued proposals now, so
a listing with nothing queued makes no extra eth_call and a failing read cannot
downgrade every proposal to its indexed status. A cold quorum cache shares one
in-flight read per key instead of firing one identical call per finished
proposal, and the latest-block retry deadline is kept outside the cache entry
so an RPC outage at boot is backed off rather than re-probed on every request.
Chain head reads without a timestamp are guarded instead of asserted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…out a timestamp

Aging the failure streak with the window disabled the consecutive-failure rule
for any upstream called less than once per window, which is exactly the set it
exists for: a relayer, a health probe, a fan-out, a quiet route. The streak is
kept and stale eviction alone reclaims slots. A chain head whose timestamp is
missing no longer discards the block number either, so block-based statuses are
still computed and only the queued comparisons are skipped. The failure rate is
rejected at boot when zero, and the fan-out test now trips a fan-out key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to anticapture-infra / anticapture-pr-2148 September 14, 2026 21:40 Destroyed
@brunod-e

Copy link
Copy Markdown
Collaborator Author

Summary of the hardening pushed today (677e5bd through 191dddc), following the review rounds:

  • Route breaker slots are reclaimable. A slot is freed when its breaker is idle, or has gone a full window without traffic with no cooldown left, so a scan of made-up paths cannot squat a DAO's 64 slots and a route that failed during an outage releases its slot afterwards. Breakers with failures in the window or a call in flight are never evicted. The consecutive-failure rule is untouched by that aging, since the keys it protects are the quiet ones.
  • Stale completions are ignored. Each breaker carries a generation bumped on every transition; outcomes from calls that started in an older generation are dropped, so the slow half of a batch cannot stretch the cooldown or reopen a recovered circuit.
  • Cleaner keys. Paths are decoded and lowercased before matching, every non-route shape lands on one <dao>:other key, the per-DAO health probe (health:<dao>) and fan-out (fanout:<dao>:<route>) have their own namespaces so probe failures and the tighter fan-out deadline never reach a DAO's proxy circuits or gateway readiness, and a DAO summary reads only the route keys the registry handed out.
  • Metric growth bounded twice. A route publishes circuit_breaker_state only once it has tripped, and each DAO may report at most 32 route names before the rest share one bucket.
  • API side. Timelock delay and grace period are read only for queued proposals, a cold quorum cache shares one in-flight read per key, the latest-block retry deadline applies at boot when nothing is cached, and a chain head without a timestamp still yields block-based statuses. Circuit breaker windows, counts, cooldowns and the failure rate are validated at boot.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 191dddc3e0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/api/src/clients/governor.base.ts
Comment thread apps/gateful/src/shared/circuit-breaker-registry.ts
… of an evicted route

Pairing a block number with a timestamp fetched separately put an RPC read back
on the warm path: a background refresh landing between the two calls replaced
the single cache entry, so the timestamp lookup for the older block missed the
cache. Callers now take both from one read through getChainHead. On the gateway
side, a route breaker that had reported OPEN and was then evicted left its
series stuck at open forever, since the gauge is synchronous and the lazy
replacement says nothing while healthy; eviction now publishes a final CLOSED
for any breaker that had published at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cafa27593

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/gateful/src/middlewares/cache.ts
Comment thread .changeset/gateful-breaker-window-stale.md Outdated
… correctly

A revalidation that ended on the stale body was counted twice, first as a miss
and then as a stale serve, so the denominator of the cache hit rate doubled
exactly while an upstream was failing. The result label is now decided once the
call has settled and incremented once. The gateful changeset also named env
vars that do not exist; it now points at the CIRCUIT_BREAKER prefix and the
config file that reads them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to anticapture-infra / anticapture-pr-2148 September 14, 2026 22:05 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2bf3c8760

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/gateful/src/shared/circuit-breaker.ts
@brunod-e
brunod-e merged commit 5f3fd7e into dev Sep 15, 2026
78 of 80 checks passed
@brunod-e
brunod-e deleted the brunod-e/proposals-resilience branch September 15, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants