Skip to content

fix(engine,db,template,settings): reliability + correctness hardening#364

Merged
rmyndharis merged 6 commits into
mainfrom
fix/audit-residuals
Jun 19, 2026
Merged

fix(engine,db,template,settings): reliability + correctness hardening#364
rmyndharis merged 6 commits into
mainfrom
fix/audit-residuals

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Summary

A batch of small, self-contained reliability and correctness fixes across the engine, template, settings, and database layers. No functional change for a correctly-configured deployment except one honest contract correction (see Behavior change below). Each change is source-traced, TDD'd, and adversarially reviewed; the full backend + dashboard gate is green (903 tests).

Changes

Engine

  • Baileys reconnect no longer leaks the previous socket. An internal (transient-drop) reconnect overwrote the live socket without tearing the old one down, leaking its WebSocket and our 9 event listeners on every reconnect. The previous socket's listeners are now detached and the socket ended before the replacement is created — and crucially the connection.update listener is detached before end() (Baileys' end() emits a synthetic close that would otherwise schedule a spurious second reconnect).
  • Engine sessions keep operator config even if the engine plugin fails to enable. The engine config blob is now also supplied to the engine plugins at construction, so createEngine still applies sessionDataPath / executablePath / authDir if enablePlugin fails before onLoad runs (previously those silently dropped to defaults). The healthy path still prefers the onLoad-set context (which carries any persisted-override merge).

Templates

  • One template name per session. A composite unique index (sessionId, name) is added via the entity and a hand-authored migration that losslessly de-duplicates any pre-existing collisions first (keeps the earliest, renames the rest to <name>-dup-<id> — never deletes a row). Resolve-by-name is now deterministic (ORDER BY createdAt ASC), and a duplicate name on create/update returns 409 Conflict. (The dual {{var}}/{var} placeholder syntax is a separate, breaking change and remains tracked in message template support #69.)

Database

  • CLI can now manage the main (auth/audit) connection. The app runs the main connection as a separate always-SQLite connection, but the default CLI DataSource only managed the data connection's migrations. Adds src/database/data-source-main.ts + migration:*:main npm scripts (purely additive; data-source.ts is unchanged). Matters when MAIN_DATABASE_SYNCHRONIZE=false.

Settings (behavior change)

  • PUT /settings is now honest. Settings are environment-derived and consumed at boot, and ConfigService is immutable at runtime, so the old handler mutated an in-memory copy and returned 200 'updated' while persisting/applying nothing — a false success. It now returns 501 Not Implemented with a clear message. The ADMIN guard and GET /settings are unchanged; the dashboard has no caller of this endpoint.

Hardening / docs

  • Cross-DB column-type/date helpers are documented as data-connection-only (the always-SQLite main connection must hardcode its types) and locked with characterization tests.
  • Documented (and locked with a test) that phone-composed self-messages are intentionally not mirrored to local history — message_create also fires for API sends, which the REST path already persists, so saving here would double-persist; safe persistence needs a unique-index + dedup and is deferred.

Behavior change

PUT /settings returns 501 instead of 200. The endpoint never actually applied changes, and nothing in the dashboard calls it, so no working flow regresses — but it is a response-status change worth a changelog note.

Testing

  • TDD throughout (red → green per change); migration verified against a real SQLite DB (lossless dedup + idempotent + unique enforcement).
  • Full gate green: backend lint + build + 903 jest tests, dashboard build + lint.
  • Adversarial multi-reviewer pass over the diff: 0 confirmed regressions.

…ck, read-only settings, cross-DB helper docs

- baileys: tear down the previous socket (detach listeners + end) before an internal
  reconnect overwrites it, preventing a per-drop WebSocket/listener leak
- engine plugins: also receive the engine config blob at construction so a session
  still gets operator config if a plugin fails to enable before onLoad runs
- settings: PUT is read-only (env-derived); return 501 instead of a false-success 200
- cross-DB column-type/date helpers: document data-connection-only intent + lock
  behavior with characterization tests
- composite unique index (sessionId, name) via entity decorator + a hand-authored
  migration that losslessly de-duplicates pre-existing collisions (keeps the earliest,
  renames the rest to <name>-dup-<id>) before creating the index
- resolve-by-name is now deterministic (order by createdAt ASC)
- create/update translate a duplicate-name constraint violation into 409 Conflict

Note: the dual {{var}}/{var} placeholder syntax is a separate, breaking change and stays
tracked in #69; only the deterministic-uniqueness half is addressed here.
The app runs the main connection as a separate always-SQLite connection, but the default
CLI DataSource only managed the data connection's migrations — so the main-owned migrations
(migrations-main) could not be run/generated from the CLI, which matters when boot
auto-migration is disabled (MAIN_DATABASE_SYNCHRONIZE=false).

Adds src/database/data-source-main.ts + migration:*:main npm scripts (purely additive;
data-source.ts unchanged). Documents the command where MAIN_DATABASE_SYNCHRONIZE is set.
message_create fires for both phone-composed and API-originated sends; the latter are
already persisted by the REST send path, so mirroring here would double-persist. Documents
the intentional omission and adds a contract-lock test so it can't change silently without
the unique-index + dedup work that safe persistence would require.
@rmyndharis rmyndharis merged commit f16c4a5 into main Jun 19, 2026
5 checks passed
@rmyndharis rmyndharis deleted the fix/audit-residuals branch June 19, 2026 18:22
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