Skip to content

Consolida o runtime Xmacna já implantado na main do fork - #1

Merged
raffareis merged 20 commits into
mainfrom
cleanup/20260919-main
Sep 20, 2026
Merged

raffareis merged 20 commits into
mainfrom
cleanup/20260919-main

Conversation

@raffareis

Copy link
Copy Markdown
Member

A main do fork estava sem os 19 commits do runtime Xmacna já implantado. A integração preserva esse runtime e os seis commits exclusivos de licença/documentação da main anterior, sem atualizar upstream.

Os três publicadores herdados do Docker Hub foram removidos; a CI usa Node24, cliente Prisma e PostgreSQL isolado. Código, schema e dependências são idênticos a 9f9f703. Produção default permanece no digest820b573e; Olympus permanece41155399. Nenhum deploy é disparado pelo merge.

Validação: lint/build,14 testescompat,37 inbox/storage,11 PostgreSQL (incluindo840entregasconcorrentes). Tags imutáveis publicadas para main anterior, rollbackrc14 e fontesdefault/Olympus. Revisão independente confirmou preservação de runtime/licença; staging e tags foram materializados antes deste push. Remoção de branches depende ainda de atualizar consumidores Elysium.

…volution-foundation#500)

Segunda visao rodada 2, regressoes 1 e 2:

- claim() no longer throws when the instance leaves enforce between the
  in-memory mode check and the claim transaction. It degrades to the live
  mode (shadow -> observe, off -> bypassed passthrough), reports
  effectiveMode so the handler refreshes its cached mode, and never drops
  the message. A throw here aborted the batch during the exact emergency
  procedure (canary rollback) that must not lose messages.
- Inbound failure propagation at the chatbot controller is now opt-in per
  integrator: only N8nController rethrows in enforce. A transient failure
  in dify/openai/typebot/evolutionBot no longer fails the aggregate
  'chatbot' sink, which replayed an n8n POST that had already delivered.

Proofs: tsc clean, lint clean, unit 17/17, PostgreSQL 11/11 x3 (skipped 0)
including new coverage: mode-degrade claim (shadow and off), updated
mode-race contract (claim never throws; blocked downgrade or clean
bypass), and per-integrator propagation contract.
…macna/elysium#518)

Problem
-------
On the Baileys route, consecutive messages from the same lead were never
coalesced and every message locked the whole instance for
debounceTime + n8n workflow time.

processChatbotDebounce returned a Promise that only resolved on the timer
flush. BaseChatbotController.emit awaited it, chatbot.controller treats n8n
as the awaited durable sink (emitDurableInbound), and BaileysMessageProcessor
is a single Promise chain per instance. So the 2nd message only entered
after the 1st buffer had already flushed. Cloud API does not go through the
queue, which is why it coalesced. Upstream v2.3.7 called n8nController.emit
without await (fire-and-forget).

Fix
---
- processChatbotDebounce now returns a synchronous acceptance receipt
  { merged, metadata, flushed }. The flush (POST to n8n + WhatsApp reply)
  runs detached from the caller with its own failure log (onFlushError);
  flushed always carries an internal catch, so ignoring it can never become
  an unhandledRejection.
- BaseChatbotController.emit no longer awaits the debounce: accepting the
  message into the buffer counts as delivery of the `chatbot` sink for the
  inbox receipt. The non-debounced branch is unchanged and still propagates
  failures in enforce mode.
- A stale timer can no longer dispatch a buffer cycle it did not schedule.
- Buffer key is unchanged (instanceName + remoteJid, evolution-foundation#522).
- The n8n payload gains messageCount, firstMessageTimestamp and
  lastMessageTimestamp. The buffer accumulates them; msg/keyId/
  messageTimestamp keep describing the LAST coalesced message. Without
  debounce the three fields describe a single message. Metadata travels on
  a shallow copy of msg, never mutating the messageRaw persisted in the
  receipt.
- Cloud API keeps coalescing; it only stops waiting for the flush before
  continuing to Chatwoot, and flush errors, which were already swallowed
  there outside enforce mode, now go to the dedicated log.

Guarantee change vs evolution-foundation#500 (deliberate)
-------------------------------------
Between acceptance in the buffer and the flush, the message lives only in
memory. If the process dies in that window, the durable receipt already
marked the `chatbot` sink as delivered and there is no replay. This is the
same risk as upstream, now explicit. Strict at-least-once would require
persisting the buffer itself in the receipt; left as a note, not a default.

Marker
------
XMACNA_DEBOUNCE_DETACHED_518 appears in code comments and in the flush
failure log string, so it survives in dist/main.js for post-deploy grep.
It deliberately does not start with XMACNA_PATCH: this is a fork source
change, not a Dockerfile build-time patch, so it does not belong in
validate-patches.sh and does not inflate its generic XMACNA_PATCH count.

Tests
-----
test/xmacna-chatbot-debounce.test.ts adds the serialized-arrival
reproduction (await the 1st emit before the 2nd, as the Baileys queue does),
which failed before this change with "aceitacao no buffer levou 201ms
(janela de 200ms)", plus a slow-flush concurrency case, metadata
accumulation, and timestamp normalization. The existing concurrent cases now
observe acceptance.flushed.
…lysium#518)

- multer ^2.3.0 and sharp ^0.35.4 (npm audit fix, in-range).
- body-parser 1.20.8 (in express 4 range) and qs override 6.16.0: express
  4.22.2 pins qs ~6.15.1, the only patched qs is 6.16.0 (semver-minor).
- deepmerge-ts override 8.0.0 for @prisma/config: the 8.0 break only
  touches deepmergeInto; prisma config uses deepmerge.
- decode-uri-component override 0.5.0 under minio's query-string 7: the
  ESM-only fix breaks query-string.parse, but minio only calls stringify;
  the compat test pins that contract.
- Compat tests exercise express urlencoded, multer memoryStorage, MinIO
  presign and deepmerge with the overridden versions.

Residual: stream-json <=3.4.0 (via minio 8.0.7). The only patched
release (3.5.0) drops jsonl/Parser.js, which minio requires at import.
…dit is clean (xmacna/elysium#518)

minio 8.0.7 pins stream-json <=3.4.0; the only patched release (3.5.0) drops
jsonl/Parser.js and breaks minio at import, so `npm audit --omit=dev` could
never be green and the inbox candidate build was blocked.

The S3 wrapper (libs/minio.server.ts, name kept so the 9 importers stay
untouched) now uses @aws-sdk/client-s3 + s3-request-presigner (same SDK
family as @aws-sdk/client-sqs already shipped):
- same exported contract: BUCKET, uploadFile, uploadTempFile, getObjectUrl,
  deleteFile; no-op when S3_ENABLED != true; upload/delete errors logged and
  returned, never thrown (caller contract);
- object keys unchanged (evolution-api/<file>); Content-Type as header, the
  rest as x-amz-meta; presign default 7 days (minio's default, SigV4 max);
- virtual-host style on *.amazonaws.com, path style elsewhere (minio parity);
- bucket bootstrap kept (HeadBucket -> CreateBucket -> public-read policy
  unless S3_SKIP_POLICY).

Removed the decode-uri-component override (only existed for minio's
query-string). Compat test now pins "minio not installed" and presigns
through the SDK. New test/xmacna-s3-storage.test.ts (7 cases, injectable
client) added to test:xmacna-inbox.

Gates: tsc OK, eslint OK, build OK, test:xmacna-compat 14/14,
test:xmacna-inbox 37/37, npm audit --omit=dev --audit-level=low: 0 vulnerabilities.
@raffareis
raffareis merged commit 2561353 into main Sep 20, 2026
5 of 6 checks passed
@raffareis
raffareis deleted the cleanup/20260919-main branch September 20, 2026 03:11
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.

1 participant