Skip to content

feat: Loop Engine + Buzz Hub on the audited base (flags off by default) - #8

Merged
LMPrado-DZ23 merged 32 commits into
release/v3.8.51from
feat/loop-buzz-on-v3851
Sep 12, 2026
Merged

LMPrado-DZ23 merged 32 commits into
release/v3.8.51from
feat/loop-buzz-on-v3851

Conversation

@LMPrado-DZ23

Copy link
Copy Markdown
Owner

Loop Engine + Buzz Hub sobre a base auditada

Traz o Loop Engine (ciclos de agente report-only com policy gate, orçamento e aprovação humana) e o Buzz Hub (ponte de eventos entre agentes via relay Nostr sobre WebSocket) da branch de trabalho fase-1-loop-buzz para a release publicada, com auditoria independente e correção de tudo o que ela apontou. Ambos nascem desligados (flags LOOP_ENGINE_ENABLED e BUZZ_HUB_ENABLED em false), e o Buzz não conecta a lugar nenhum até um operador configurar um relay.

Como veio

Transplante seletivo: 12 commits da branch antiga aplicados por cherry-pick -x sobre release/v3.8.51, sem conflito. Ficaram fora de propósito: o vendor chatgpt-web v4.0.7, as "superpowers" da Fase 2 (PII BR, review gate do MCP, Browser Guard/AG-UI, OTel) e o endpoint de stream AG-UI do Loop, que depende delas.

Auditoria independente e o que ela mudou

Dois auditores revisaram o resultado sem ver as conclusões um do outro (audit/LOOP_BUZZ_REVIEW.md). Veredito inicial: arquitetura com ressalvas, segurança reprovada. Corrigido na causa raiz, um problema por commit, com teste que falha antes e passa depois:

Segurança

  • A identidade Nostr do agente era gravada em texto puro. Agora é cifrada em repouso com o mecanismo existente, migra o valor legado na primeira leitura, entra na auditoria de criptografia e falha fechado quando o perfil exige chave. Só a chave pública sai da API.
  • A URL do relay só era conferida contra o prefixo do protocolo: aceitava credenciais embutidas, hosts privados e o endereço de metadados da nuvem. Agora passa por um guarda que rejeita userinfo/query/fragmento, bloqueia metadados sempre, bloqueia faixas privadas fora de loopback e exige TLS fora de loopback.
  • Quatro rotas mutáveis liam o corpo sem schema, o que tornava o orçamento do Loop anulável por um token de escrita. Todas validam com schemas estritos. O gate que deveria ter pego isso tinha uma expressão que não casava com o nome do parâmetro usado; foi corrigido, e as duas rotas pré-existentes que ele passou a detectar ficaram congeladas com verificação de obsolescência.
  • approveStep aprovava qualquer etapa em qualquer estado sem reavaliar o policy gate: um efeito negado (apagar, comprar) virava "aprovado". Agora exige estado e etapa corretos e recusa efeitos negados.
  • Mutações de /api/loop e /api/buzz exigem escopo de administrador, não apenas escrita.
  • Erros deixaram de devolver err.message cru (que podia conter host e porta do relay).

Confiabilidade

  • Entradas do outbox marcadas como falhas nunca eram retentadas. Agora há backoff exponencial com variação aleatória, teto de cinco tentativas e status dead próprio para a falha permanente, separado no painel das que ainda serão retentadas.
  • A conexão resolvia por tempo fixo, sem esperar a resposta de autenticação do relay: a primeira publicação podia falhar para sempre.
  • O consumidor do inbox não tinha chamador: nada chegava a ser recebido. Agora sobe no boot atrás da flag, reconecta com backoff, nunca derruba a inicialização e encerra limpo no shutdown.
  • O adaptador WebSocket ganhou limite de payload, validação de forma do evento antes de verificar assinatura, tratamento de fechamento e erro, e prazo total no flush.
  • advance era leitura-modificação-escrita sem guarda: duas requisições concorrentes perdiam uma transição. Agora o número de sequência funciona como versão, com conflito explícito.
  • Rejeitar uma etapa fazia o ciclo voltar ao início e girar até estourar o orçamento; agora escala. O teto de tempo é medido pelo motor, não apenas informado por quem chama.

Produto

  • As duas páginas do painel estavam com textos fixos em português num produto que é inglês-primeiro, sem confirmação para aprovar/rejeitar, sem identificadores de teste e com funções muito acima do limite de complexidade. Agora usam o catálogo de tradução (41 locales), confirmam decisões destrutivas, expõem identificadores estáveis e estão divididas em componentes.
  • As seis rotas novas entraram na especificação OpenAPI com os limites reais lidos dos schemas.

Verificação

19 gates de qualidade verdes (dependências, numeração de migração, guarda de rotas, ciclos de importação, cobertura de mutação, contrato de ambiente, validação de rotas, documentação, OpenAPI e outros) e 236 testes unitários do escopo, mais 21 de interface e 25 de contrato de i18n. Duas dependências novas (@noble/curves, @noble/hashes, assinatura Nostr) fixadas e na allowlist de supply chain.

Pendências honestas

  • O gate check:openapi-security-tiers falha por uma divergência pré-existente em rotas da volcengine, idêntica antes desta branch (comprovado trocando o arquivo pelo da base).
  • oasdiff não está instalado localmente, então o gate de quebra de contrato se auto-ignora; as adições são puramente aditivas.
  • O endpoint de stream do Loop e as demais superpowers da Fase 2 continuam fora; nada aqui depende delas.

🤖 Generated with Claude Code

zodyprado-web and others added 28 commits September 12, 2026 06:21
Modulo LEVE derivado de Loop Engineering (MIT, 1d1af34b). Logica pura, sem efeitos
externos, atras da flag loop_engine (OFF). Configuracao no PAINEL UNICO do OmniRoute.

- types.ts: fases discover..escalate, LoopRun/Step/Budget/Verdict, PolicyDecision.
- budget.ts: controle de orcamento (estourou -> aborta), pressao, soma imutavel.
- policyGate.ts: DETERMINISTICO (codigo decide, nao IA). Efeito destrutivo=deny;
  report-only ou kind sensivel=require_approval; so allow se operador liberou e nao destrutivo.
- stateMachine.ts: transicao pura fail-closed; efeito nao-aprovado -> awaiting_approval;
  verifier reprova -> repete limitado por attempts, senao escalated (handoff humano).
- index.ts: createLoopRun/proposeStep + API. Estado duravel ira no DB do OmniRoute (migracao 175).

Teste: tests/unit/loop-engine-core.test.ts = 8/8 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit ae47555)
…ado)

Adaptador tipado OmniRoute<->Block Buzz (Apache-2.0, 3c7f288), atras da flag buzz_hub (OFF).
Buzz e servico SEPARADO (relay Nostr); nada conecta enquanto desabilitado.

- types.ts: BuzzEvent (NIP-01), OutboxEntry/InboxEntry, BuzzAdapter, IdentityMapping.
- outbox.ts: Outbox/Inbox idempotentes (dedup por event.id, ordenacao por sequence).
- adapter.ts: DisabledBuzzAdapter (inerte) + guarda de seguranca — chave Nostr NUNCA autoriza
  no OmniRoute (nostrKeyAuthorizes()===false; buzzIdentityIsMapped fail-closed).
- index.ts: resolveBuzzAdapter(flag). WebSocketBuzzAdapter real entra com relay+disco+flag ON.

Teste: tests/unit/buzz-bridge-core.test.ts = 7/7 pass. Sem dupla fonte de verdade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 499ac5b)
…INE/BUZZ_HUB

- Migracao 175 (aditiva/idempotente/nao-destrutiva): tabelas loop_runs, loop_steps,
  buzz_outbox, buzz_inbox (estado duravel dos modulos agentic). Validada: 4 tabelas,
  aplicavel 2x sem erro, insert/read OK.
- featureFlagDefinitions: +LOOP_ENGINE_ENABLED e +BUZZ_HUB_ENABLED (category runtime,
  default OFF) — aparecem no PAINEL UNICO do OmniRoute para ligar/desligar.

Aditivo, sem regressao. Modulos permanecem inertes ate flag ON (e, no Buzz, relay disponivel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 86b44b4)
…real, testados)

Sai do "nucleo puro" para modulo funcional, gravando/lendo no DB do OmniRoute (tabelas 175):
- src/lib/db/loopEngine.ts: saveLoopRun (upsert transacional run+steps), getLoopRun,
  listLoopRuns. Round-trip cria->salva->carrega->advance->re-persiste. 3/3 testes.
- src/lib/db/buzzBridge.ts: enqueueOutbox (idempotente por event.id), pendingOutbox,
  markOutbox, receiveInbox (dedup). Outbox DURAVEL mesmo sem relay. 2/2 testes.

Report-only; nenhum efeito externo. Fonte de verdade = DB do OmniRoute. Aditivo, sem regressao.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 5473461)
…esses)

Torna o Loop Engine utilizavel de ponta a ponta (report-only, autenticado, gated por
LOOP_ENGINE_ENABLED):
- src/lib/loopRunner.ts: startRun/addStep/advanceRun/approveStep/rejectStep/
  runsAwaitingApproval. Liga nucleo+repositorio; gate segura efeito -> awaiting_approval;
  destrutivo -> deny. Teste tests/unit/loop-runner.test.ts = 3/3.
- src/app/api/loop/route.ts (GET lista / POST inicia), [id]/route.ts (GET run),
  [id]/advance (POST avanca), [id]/approve (POST aprova/rejeita etapa).
  Todas: requireManagementAuth + flag gate; typecheck 0 erros.

Nenhum efeito externo executado aqui. Aditivo, sem regressao.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 94ad156)
…elay (Nostr)

Integracao funcional OmniRoute <-> Buzz (relay Nostr) — provada contra o relay real:
- deps: +@noble/curves +@noble/hashes (schnorr secp256k1 + sha256, pure-JS auditadas).
- nostr.ts: assinatura/verificacao de eventos NIP-01 (id=sha256, sig=schnorr). Teste 3/3.
- wsAdapter.ts: WebSocketBuzzAdapter (ws) com auth NIP-42, publish de evento assinado e
  subscribe. resolveBuzzAdapter(flag, config) troca do inerte para o real quando ha relay.
- Prova E2E contra buzz-prod-relay (ws://localhost:3000): connect + auth + publish OK=true.
  (Host: usar localhost, nao 127.0.0.1 — o relay resolve comunidade por host.)

Agentes publicam no Buzz -> clientes Nostr no celular veem. Aditivo, flag buzz_hub OFF por padrao.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit dc033ee)
… adapter + flush do outbox)

Fecha o fluxo OmniRoute -> Buzz relay, provado ao vivo (enqueue -> flush -> published=1):
- getOrCreateAgentSecretKey: identidade Nostr do agente, persistida em key_value (estavel).
- getBuzzConfig: relayUrl (BUZZ_RELAY_URL, default ws://localhost:3000) + chave. Config no painel.
- getBuzzAdapter/flushBuzzOutbox: gated por BUZZ_HUB_ENABLED; publica pendentes no relay real
  (idempotente, dedup do relay por event.id). Skipped quando flag OFF (CI-safe).

Testes: buzz-service 3/3 (CI). Integracao ao vivo contra buzz-prod-relay: published=1, outbox drenado.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 9ca2ed8)
Registra os superpoderes agentic (Loop report-only + Buzz colaboracao Nostr) para o
changelog do release. Aditivo, feature-flags OFF, painel unico, migracao 175.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit fffba41)
Superpoderes agora operáveis a partir do painel único do OmniRoute
(seção Agentic Features), além do toggle das flags:

- Loop Engine (/dashboard/loop): lista ciclos report-only, inicia run por
  padrão, avança 1 passo (report-only) e aprova/rejeita etapas que aguardam
  aprovação humana. Consome os endpoints /api/loop já existentes. Mostra CTA
  para ativar LOOP_ENGINE_ENABLED quando desligado.
- Buzz Hub (/dashboard/buzz): estado do relay, pubkey do agente (nunca a
  secreta), contagens do outbox/inbox e botão "Publicar pendentes". URL do
  relay agora configurável PELO PAINEL (override em key_value com precedência
  painel→env→default), honrando "um painel configura tudo".

Novos endpoints management, gated e aditivos:
- GET/PUT /api/buzz (status + set relay url)
- POST /api/buzz/flush (flush do outbox; skip com a flag OFF)

buzzService: getBuzzStatus/getBuzzRelayUrl/setBuzzRelayUrl; db/buzzBridge:
buzzCounts(). Itens de sidebar sempre visíveis (gate real é server-side) com
CTA de ativação — mais descobrível.

typecheck:core = 0 erros. Fase 1: 31 testes verdes (+2 no buzz-service).
Aditivo, sem regressão. A chave Nostr nunca autoriza ação no OmniRoute.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 406ea81)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit aa938be)
…IGH)

Correções de causa-raiz dos MEDIUM/LOW acionáveis (auditorias Arch/Sec/Prod):

- Loop (A-M1): a máquina de estados AGORA conta a tentativa na reprova do verifier —
  o teto de tentativas é auto-imposto pelo motor, não depende do chamador passar
  consumed.attempts. Novo teste prova escalonamento sem hand-set (era mascarado).
- Buzz (A-M2b): enqueue carimba createdAt estável e persistido; publish usa o
  createdAt persistido (nunca o relógio) → re-assinatura idempotente (mesmo id →
  dedup real do relay). Novo teste cobre. Removida flag `authed` morta (A-L2).
  publish re-assina com a chave do agente por design (A-M2c, documentado).
- Painel (C-1): subtítulos do sidebar em PT-BR (fim da mistura de idioma).
- Painel (C-2): a11y — aria-label/htmlFor+id nos inputs, aria-expanded no toggle,
  aria-hidden nos ícones decorativos.
- Painel (C-3): advance/approve/reject mostram erro em vez de engolir a falha.
- Painel (C-4): FeatureFlagsGrid semeia a busca do ?q= (CTAs Loop/Buzz agora
  pré-filtram a flag).
- Painel (C-5): botão "Publicar pendentes" desabilitado com a flag OFF.
- API (B-1): documentado que PUT /api/buzz é config-before-enable (não gated, não
  conecta, admin-only).

typecheck:core=0, Fase 1 33/33 (2 testes novos), lint=0. Aditivo, sem regressão.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 9ab17b1)
…ant da auditoria)

- Migração 175: tabelas loop/buzz nascem com tenant_id + índices por tenant
  (CLAUDE.md §5.1). Migração não-lançada (PR aberto) → editada na origem.
- Repos escopados por tenant (DEFAULT_TENANT='default'): saveLoopRun/getLoopRun/
  listLoopRuns e enqueueOutbox/pendingOutbox/receiveInbox/buzzCounts. Isolamento
  real: get/list de outro tenant não vazam.
- Produtor (buzzProducer): ao ENTRAR em awaiting_approval/escalated, o Loop
  enfileira um aviso durável no outbox (best-effort, report-only) — para o humano
  ver no celular. Só na transição; idempotente por id.
- Consumidor (buzzConsumer): startBuzzInboxSubscription assina o relay (flag ON) e
  roteia eventos para receiveInbox — STORAGE-ONLY. Nostr nunca autoriza ação.

typecheck:core=0. Suíte Loop+Buzz 36/36 (+3: produtor + 2 isolamento por tenant).
Aditivo, sem regressão, atrás das flags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit f42d008)
…in the mission state

Records the operator authorization, the selective transplant (12 cherry-picked commits, no conflicts), what was deliberately left out (chatgpt-web vendor, Fase 2 superpowers, AG-UI stream endpoint), the two new signing dependencies and the verification plan.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… for the Buzz bridge)

check:deps (supply-chain gate) requires every new dependency to be reviewed and listed. Both packages are the audited noble cryptography libraries by paulmillr (registry maintainer verified, repository github.com/paulmillr/noble-{curves,hashes}), pinned at 2.4.0 with sha512 integrity in package-lock.json; they are used only by open-sse/buzz-bridge/nostr.ts to sign/verify Nostr events.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…uzz bridge)

check:docs-counts (strict) — README.md, AGENTS.md, llm.txt and the 41 llm.txt mirrors state the real count.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tate inside useEffect

The lint guard (react-hooks/set-state-in-effect, --max-warnings=0) rejects the mount effect calling load(), which sets loading/error synchronously. Both pages now use the base pattern: a pure fetch helper awaited inside an async IIFE with a cancelled flag, state written only after the response, and load() kept for user-triggered refreshes. Behaviour unchanged; eslint clean, dashboard tsc clean for both files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Verdict COM RESSALVAS: H1 lint (fixed in 6986295), H2 Buzz consumer never wired, H3 outbox has no retry path and connect() races the relay AUTH; M1 unvalidated loop budget, M2 no optimistic guard on advance, M3 new complexity debt, M4 panel outside base patterns (i18n/ConfirmModal/testids), M5 docs (OpenAPI, BUZZ_RELAY_URL, changelog fragment name), M6 Nostr secret stored in plaintext; L1-L8.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Verdict REPROVADO for publication in the transplanted state: H1 Nostr secret key stored in plaintext outside encryption-at-rest and its audit; H2 the T06 route-validation gate is bypassed (regex matches request.json( only) and 4 mutable routes have no schema, so the loop budget is nullable by a write token; H3 approveStep ignores the policy gate; M1 no admin-scope line in the authz matrix for /api/loop and /api/buzz mutations; M2 relay URL accepts embedded credentials, private hosts and cloud metadata; M3 ws adapter without maxPayload/shape validation/deadline; M4 raw err.message to clients; M5 wall-clock budget not measured; M6 i18n keys missing; L1-L5, I1-I3. Fix loop in progress (two fixer agents, Loop and Buzz scopes).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tedAt, split advance into named steps, drop orphan exports

Auditor A L1/L2/M3/L6 (audit/LOOP_BUZZ_REVIEW.md): a rejected step with no new proposal fell back to report_only and cycled until the budget (now escalated); maxWallClockMs was only honoured through the caller's consumed field (the engine now enforces max(consumed, now - createdAt)); advance had complexity 17 / cognitive 26 and is now applyUsage/applyBudget/applyRejection/applyPolicyGate/applyVerdict/finishOrNext (0 ratchet messages); budgetPressure, isAutoExecutable, runsAwaitingApproval and dead re-exports removed, emptyUsage used by createLoopRun. RED-first cases in loop-engine-core: 61 s past createdAt -> aborted; rejection -> escalated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…stic version, make approveStep honour the policy gate, typed errors

Auditor A M1/M2/L7 and Auditor B H2/H3/M4: POST /api/loop spread an unvalidated budget into LoopBudget ('x' or negative values disabled the brake) — the three mutable routes now use validateBody with strict zod schemas (integer budgets with ceilings, pattern/taskId/correlationId bounded, decision enum); saveLoopRun was last-writer-wins across processes — sequence_number is now the run version, every runner mutation runs in a transaction and bumps it, the UPDATE is guarded by tenant + expected version (LoopRunConflictError -> 409, advance accepts expectedSequenceNumber); approveStep accepted any step in any state and never re-ran decideEffect — it now requires awaiting_approval + a proposed step and refuses denied effects (409); advance/approve no longer return raw err.message or 404 for every failure (loopErrorResponse: 400/404/409/500). RED-first: 10 invalid budgets -> 400, concurrent save -> conflict, approve delete/purchase -> refused, cross-tenant upsert -> no overwrite. 82/82 across the loop, route and scope tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…validation gate detects req.json() too

Auditor B M1/H2: /api/loop and /api/buzz mutations only required the write scope — added to ADMIN_MUTATION_PREFIXES (RED: inferRequiredScope POST /api/loop/x/approve was write, now admin; GET stays read; /api/loopback-thing lookalike stays write). The T06 route-validation gate only matched request.json( and silently skipped every handler that names the parameter req — the regex now matches both; the two pre-existing routes it newly detects (logs/detail, providers/[id]/login) are frozen in KNOWN_UNVALIDATED_ROUTES with a stale-check so only new gaps fail. The gate currently reports src/app/api/buzz/route.ts, which the Buzz fix in progress closes.

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

The count-pinning test documents every bump; the two Loop/Buzz flags were added by the transplanted commits without updating it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, harden the WebSocket adapter, typed errors

Auditor B H1/L2/M2/M3/M4/L5 + T06: the agent secret key was written in plaintext to key_value (now encryptSensitive/decrypt, legacy plaintext migrated on first read, fail-closed EncryptionUnavailableError when the profile requires a key, row added to the storage-encryption audit); GET /api/buzz minted and persisted the identity as a read side effect with the flag off (now read-only until the hub is enabled); the relay URL was only checked against ^wss?:// (validateBuzzRelayUrl: URL parse, no userinfo/query/fragment, cloud-metadata always blocked, private hosts blocked except loopback, wss:// required off loopback — applied on PUT, on the persisted override, on the env default and in the adapter constructor); the ws adapter had no maxPayload, no shape validation, no close/error handlers and orphan timers (1 MiB cap, perMessageDeflate off, handshake timeout, isWellFormedRelayEvent before schnorr, readyState guard, timers cleared/unref'd, connect() resolves on the NIP-42 AUTH OK or a 3 s timeout, flush with a 30 s total deadline); flush returned err.message with the relay host (now a generic 502 with code relay_unavailable, detail only in redacted logs); PUT body goes through validateBody so the T06 gate covers it. RED-first: 8 encryption cases, 27 URL cases, 7 adapter cases against a fake relay, 17 route cases. Default relay is now empty (opt-in) — ws://localhost:3000 collided with the Next dev port.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…x consumer at boot with a shutdown hook

Auditor A H2/H3/L6: entries marked failed were never retried (pendingOutbox only read pending) — migration 176 adds next_attempt_at; pendingOutbox now includes failed entries with attempts < 5 whose backoff (30 s × 2^(n-1), 30 min cap, ±25 % jitter) has elapsed, and markOutbox(failed) schedules the next attempt; the inbox consumer had no caller — initBuzzInboxConsumer() runs from instrumentation-node behind BUZZ_HUB_ENABLED with a valid relay, registers the buzz-inbox shutdown hook unconditionally, reconnects with backoff and never fails the boot; the producer no longer mints an identity; the unused in-memory Outbox/Inbox classes and the buzzIdentityIsMapped/nostrKeyAuthorizes exports are removed. RED-first: auth-required relay publishes after AUTH, rejected entries retry per flush up to 5 then stop, received event lands in buzz_inbox, graceful shutdown closes the socket, flag off connects nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s blocked); migrations count 173

Auditor A/B L3: the variable read by buzzService was undocumented (env/docs contract gate on Linux). Migration 176 bumps the strict docs count.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two independent reviewers flagged that keeping entries in 'failed' after the retry ceiling conflates transient failures still scheduled for retry with permanent ones: the panel counter grows forever and an operator cannot tell them apart or clean them up. markOutbox now sets status='dead' with next_attempt_at NULL on the last attempt, pendingOutbox keeps ignoring both, and buzzCounts exposes outboxDead next to outboxFailed. The retry test asserts the terminal row is dead with no schedule and that the counters split (failed 0 / dead 1).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ve decisions, split them into components

Auditor A M3/M4/L5 and Auditor B M6: both pages shipped with hardcoded pt-BR strings in an EN-first product, no keys for the sidebar entries or the two feature flags, no confirmation on approve/reject or on saving a relay URL, no test ids, and single functions of 256 and 223 lines over the complexity ceiling. They now use useTranslations against new en.json keys (propagated to the 41 locales, pt/pt-BR/vi translated), wrap approve/reject/save in ConfirmModal, expose stable data-testid hooks, and are split into RunCard/StepRow/StartRunForm/DecisionConfirmModal/ErrorBanner/FlagDisabledNotice/LoadingState and BuzzCounts/IdentityCard/RelayUrlCard/FlushControls with useLoopRuns/useBuzzHub owning the data. Failures render translated messages instead of a raw err.message, the flag-off state explains how to turn the feature on, and the status types mirror the API (agentPubkey may be null, relayUrl may be empty, counts carry outboxDead). The load result is discriminated by a string tag because the project compiles with strict:false, where a boolean discriminant does not narrow. 21 vitest cases across the two pages, 25 i18n contract cases, dashboard typecheck clean for both directories.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… OpenAPI spec

Auditor A M5 / Auditor B L4: the routes shipped undocumented while the changelog announced them. Adds the Loop Engine and Buzz Hub tags, the six paths with the real request schemas read from the zod definitions (budget ceilings, bounded pattern and ids, expectedSequenceNumber), the 404 a disabled flag returns, the 409 conflicts, the typed Buzz 400 codes and the 502 relay_unavailable, plus the shared responses and 17 component schemas. The changelog fragment is renamed with its PR prefix and rewritten for the final state. check:api-docs-refs, check:docs-all, fabricated-docs --strict and changelog-integrity pass; openapi-coverage 99.3 %.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 0804806c-88e4-4fa2-acc2-0f328fcda460


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…s in both env files

The Linux Docs Gates failed on the env/docs contract: ENVIRONMENT.md named BUZZ_HUB_ENABLED in the BUZZ_RELAY_URL description while .env.example never listed it. Both flags are genuinely settable through the environment (featureFlags resolves database override, then process.env, then the default), so each now has a commented line in .env.example and a row in the reference table stating that it ships off and that a panel override wins. check:env-doc-sync and check:docs-all exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zodyprado-web and others added 3 commits September 12, 2026 08:42
… dead buzz re-exports

Two red gates on PR #8, both root-caused rather than accommodated.

Unit Tests fast-path (1/4): `9147-catalog-eventloop-yield` failed with HTTP 500
where it expects 200. The 500 was not the builder crashing. The test seeds a
deliberately oversized catalog (60 connections / 720 models) and the build took
8349 ms on the hosted shard, crossing the 8 s cold-path coalescing bound in
catalogCache.ts. That wait rejects with `catalog_build_timeout`, a cold cache
has no last-good body to serve instead, and the wrapper answers a sanitized 500
without logging — which is why the shard log carried no error. The test now
raises CATALOG_BUILD_TIMEOUT_MS for its own process; the bound and its last-good
fallback keep their own coverage in 12627-catalog-inflight-timeout.

The file also moved to tests/unit/serial/ (the diegosouzapw#6803 bucket, --test-concurrency=1).
It measures how long the builder holds the event loop, and three sibling test
processes on one runner starve its timer loop, so the recorded gap stopped being
the builder's. The 800 ms bound is unchanged; the signal behind it is now clean.
A non-200 now reports the response body instead of bare `500 !== 200`.

Fast Quality Gates (dead-code): the buzz-bridge barrel re-exported seven symbols
nothing imports through it — the event-shape limits, the two Nostr event
interfaces and the relay-url result union. Every real consumer reaches for them
in their own module. Removed from the barrel; the modules still export them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…al path

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cache catalog finding

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LMPrado-DZ23
LMPrado-DZ23 merged commit dac7708 into release/v3.8.51 Sep 12, 2026
5 checks passed
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