feat(auto-ack): message-type × hop-distance 2×2 matrix (#3564)#3580
Conversation
Auto-Acknowledge previously tangled two concepts: the "Direct" toggles actually meant "0 hops" (not direct messages), tapback/reply were keyed only on hop distance (shared across channel & DM), DMs were gated by a separate autoAckDirectMessages flag, and a single global autoAckUseDM re-routed channel replies to DM. Discussion #3564 asked for the four real combinations to be configurable independently. Replace it with a clean {Channel,Direct} × {ZeroHop,MultiHop} matrix. Each of the four cells independently configures Message (reply), Tapback (emoji reaction), and Respond-via-DM. "Respond via DM" applies to the reply only (tapback-via-DM is unreliable) and is disabled until Message is enabled; Direct cells are inherently DMs so it's shown checked+disabled there. - Server: rewrite checkAutoAcknowledge around the matrix; extract the cell-selection and reply-routing into a pure, unit-tested utils/autoAckDecision.ts. 12 new per-source settings keys. - Migration 093 folds each source's legacy config into the matrix so existing behavior is preserved on upgrade (pure core unit-tested). - Frontend: utils/autoAckMatrix.ts (typed model + round-trip helpers), AutomationContext (structured autoAckMatrix), AutoAcknowledgeSection rewritten to a 2x2 grid, App wiring, tests, i18n. - MeshCore auto-ack is unchanged (separate, simpler path). Full Vitest suite: 6983 passed, 0 failed. tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVGxkuD4Fwa2JGVim8ZeVj
|
Claude finished @Yeraze's task —— View job PR Review: feat(auto-ack): message-type × hop-distance 2×2 matrix
This is a well-structured refactor that cleanly replaces a tangled legacy design with an explicit 2×2 matrix model. The separation of concerns — pure decision logic extracted to Potential Bugs / Correctness1. Message template selection uses raw In const autoAckMessage = (hopsTraveled === 0 && autoAckMessageDirect)
? autoAckMessageDirect
: autoAckMessageStandard;This diverges from the matrix's cell classification, which uses 2. Migration skips prefixes that have only From const hasBehaviorKey = LEGACY_SUFFIXES.some(
(s) => s !== 'autoAckEnabled' && legacy[s] !== undefined,
);
if (legacy.autoAckEnabled !== 'true' && !hasBehaviorKey) continue;
3. In replyId: cellReplyDmEnabled && !isDirectMessage ? undefined : packetId,
Code Quality4. In const onDefaultTrue = (v: string | undefined) => v === undefined ? true : v !== 'false';This means 5. The skipped test suite is a meaningful gap
6.
const enabledChannels = autoAckChannels
? autoAckChannels.split(',').map(c => parseInt(c.trim())).filter(n => !isNaN(n))
: [];
Performance7. Six sequential After the matrix cell is determined, the code makes three sequential DB calls ( Minor Nits
SummaryThe core design is sound. The most actionable items are:
|
Summary
Implements discussion #3564. Auto-Acknowledge previously tangled two independent concepts: its "Direct" toggles actually meant 0 hops (not direct messages), the tapback/reply toggles were keyed only on hop distance (shared across channel & DM), DMs were gated by a separate
autoAckDirectMessagesflag, and a single globalautoAckUseDMre-routed channel replies to DM. This replaces that with a clean {Channel, Direct} × {0-hop, Multi-hop} matrix where each of the four cells is configured independently.What you get
Each of the 4 cells has three independent toggles:
Rules:
Changes
Server
meshtasticManager.ts—checkAutoAcknowledgerewritten around the matrix (cell = type × hop; MQTT-relayed packets are never 0-hop). The channel allowlist still gates channel messages; DMs are now gated by whether the Direct cells enable anything (no separate master).src/server/utils/autoAckDecision.ts(new) — pure, unit-tested cell-selection (autoAckCellKey,autoAckIsZeroHop) and reply-routing (resolveAutoAckReplyRouting, incl. the channel→DM "can't thread, clear replyId" rule).src/server/constants/settings.ts— 12 new per-source keys (autoAck{Channel,Direct}{ZeroHop,MultiHop}{Reply,Tapback,ReplyDm}Enabled), in both VALID and PER_SOURCE lists.Migration
093_autoack_matrix.ts(new) — folds each prefix's (global + everysource:<id>:) legacy auto-ack config into the matrix so existing behavior is preserved on upgrade; idempotent (insert-if-absent). Pure core (computeMatrixValues/computeMigrationInserts) is unit-tested. Registered;migrations.test.tsbumped to 93.Frontend
src/utils/autoAckMatrix.ts(new) — typed model (AutoAckMatrix,AUTOACK_CELLS) +matrixToSettings/settingsToMatrixround-trip helpers (unit-tested).AutomationContext.tsx— replaces the 10 flat legacy vars with a structuredautoAckMatrix.AutoAcknowledgeSection.tsx— the old Direct/Multihop sections become a 2×2 grid; per-cell checkboxes; the component's own save POSTs the 12 keys.App.tsxwiring,en.jsonstrings, component tests (incl. a new test that "Respond via DM" is disabled until "Message" is checked).Docs: CHANGELOG entry, CLAUDE.md migration count → 93, design note (
docs/internal/dev-notes/AUTOACK_2X2_PLAN.md).MeshCore auto-ack is a separate, simpler path and is unchanged.
Issues Resolved
Implements discussion #3564.
Testing
autoAckDecision(cell selection + routing),autoAckMatrix(round-trip + 12 key names),093_autoack_matrix(legacy→matrix mapping incl. per-source prefixes), migrations registrytsc --noEmitclean on all touched filesautoAckDirect*= 0-hop,autoAckMultihop*= multi-hop,autoAckUseDM→ Channel cells' Respond-via-DM,autoAckDirectMessagesgates the Direct column)🤖 Generated with Claude Code