Skip to content

Commit 9b972b8

Browse files
Yerazeclaude
andauthored
refactor(auto-responder): extract Meshtastic DSL matcher — tests now guard production code (#3962) (#4011)
* refactor(auto-responder): extract Meshtastic DSL matcher into matchAutoResponderPattern The ~130-line placeholder-DSL matcher ({name}/{name:regex} brace-parse, capture-regex build, param extraction with Unicode preservation) was duplicated verbatim from checkAutoResponder in meshtasticManager.ts into its own test file. Any change to production silently diverged from 60+ assertions. WP1: Move the pure logic into matchAutoResponderPattern(patternStr, messageText): AutoResponderMatch in src/utils/autoResponderUtils.ts, next to its pure siblings (splitTriggerPatterns, normalizeTriggerPatterns, normalizeTriggerChannels). Wire checkAutoResponder to call it — behavior byte-identical (homoglyph normalization, DSL, capture regexes, Unicode param extraction, case handling, compileUserRegex ReDoS guard all preserved). Remove orphaned normalizedText variable. WP2: Retarget meshtasticManager.autoresponder-regex.test.ts — delete the verbatim copy (extractParameters + testTriggerMatch, lines 16-155), import matchAutoResponderPattern, adapt all ~61 assertions to {matched,params}. Tests now guard production code, not a local copy. MeshCore checkAutoResponder: intentionally untouched (different algorithm, different schema, no shared executable logic with Meshtastic). Epic bookkeeping: check off 2.2c in REMEDIATION_EPIC.md; log 2.2b entry (PR #4009 / hotfix PR #4007). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AphLfgUJWaFNAgU5UXdJ4n * fix(build): house the DSL matcher server-side — safeRegex's native re2 broke the Vite client build src/utils/autoResponderUtils.ts is imported by frontend TriggerItem.tsx; adding homoglyph/safeRegex imports there pulled the native re2 binary into the client bundle graph (UNLOADABLE_DEPENDENCY). The matcher now lives in src/server/utils/autoResponderMatcher.ts with a comment explaining the constraint; autoResponderUtils.ts restored to its import-free pre-PR state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AphLfgUJWaFNAgU5UXdJ4n --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 5bcb4ff commit 9b972b8

4 files changed

Lines changed: 310 additions & 417 deletions

File tree

docs/internal/dev-notes/REMEDIATION_EPIC.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Phase 2 started 2026-07-07. Decisions carried forward from the Phase 0+1 intervi
3434
- [x] **2.1**`MeshCoreManager implements ISourceManager`: one unified `sourceManagerRegistry` for all source types; `meshcoreRegistry.ts` reduced to a `@deprecated` shim (delete after one release). WP1 (interface+guards+config move) → WP2+WP3a (lifecycle+loop hardening) → WP3b (read-site migration) → WP4 (shim+tests+docs). See `task21_spec.md` and PR on branch `feature/3962-p2-meshcore-isourcemanager`.
3535
- [x] **2.2a** — Heartbeat/status-probing: extract shared service parameterized by `ISourceManager` (plan §2.2 ¶1).
3636
- [x] **2.2b** — Auto-announce: MeshCore + Meshtastic cycles → one `autoAnnounceService` with per-protocol adapters (plan §2.2 ¶2).
37-
- [ ] **2.2c** — Auto-responder: `checkAutoResponder` in both managers → shared service (plan §2.2 ¶3).
37+
- [x] **2.2c** — Auto-responder: `checkAutoResponder` in both managers → shared service (plan §2.2 ¶3).
3838
- [ ] **2.2d** — Distance-delete scheduling: MeshCore's `DistanceDeleteScheduler` construction → unified ownership in `services/` (plan §2.2 ¶4).
3939
- [ ] **2.3** — Singleton retirement: enumerate legacy-singleton branches in `meshtasticManager.ts`; create a registry-managed default source from env config; reduce `export default` to a pure alias; delete last special-casing (plan §2.3).
4040

@@ -63,4 +63,5 @@ Record per-phase: PR link, deviations from plan, follow-ups.
6363
- **1.6** — PR TBD (schemaDrift.test.ts + schemaDrift.allowlist.ts; 15-entry allowlist; census confirmed 15 divergences — 10 onlyInBootstrap, 3 onlyInReplay, 2 sqlMismatch — matching the architect spec exactly). Finding flagged for Phase 3.3: 7 createIndexes()-only indexes (idx_messages_createdAt/fromNodeId/toNodeId, idx_nodes_updatedAt, idx_route_segments_distance/recordholder/timestamp) are silently lost on a replay-only fresh install; Phase 3.3 must add migrations creating each before deleting createIndexes(). No deviations.
6464
- **2.1** — PR #4004 merged (unified registry; ISourceManager on both managers; meshcoreRegistry → deprecated shim; ensureMeshCoreManagerStarted helper). Adversarial review (8 finders/11 verifiers) caught a confirmed by-id crash class (meshcore ids reaching Meshtastic-only routes via resolveSourceManager/security routes/purge-node) — fixed at shared-resolver altitude with regression tests. Backlog noted: disconnect() idempotency guard, notifyNewNodeDiscovered name fetch, start() contract hardening.
6565
- **2.2a** — PR #4005 merged (HeartbeatScheduler leaf; MeshCore adopts; premise correction documented — no cross-protocol heartbeat duplication). Premise correction: no cross-protocol heartbeat duplication exists (Meshtastic = transport-level keepalive push; MQTT = none) — reduced scope to a HeartbeatScheduler leaf utility adopted by MeshCore only; reconnect machinery untouched (Phase 4.2). New files: `src/server/services/heartbeatScheduler.ts` (protocol-agnostic scheduler: interval/start/stop/in-flight-guard/pre+post-await connected-gate) + `heartbeatScheduler.test.ts` (8 fake-timer unit tests covering every gate). MeshCore adoption: `startHeartbeat`/`stopHeartbeat` delegate to per-instance HeartbeatScheduler; `runHeartbeatProbe` deleted; `heartbeatTimer`/`heartbeatProbeInFlight` fields removed; `heartbeatProbe`/`onHeartbeatOk` added as manager callbacks; `recordHeartbeatFailure`/`beginReconnect` chain unchanged. New integration test in meshcoreNativeBackend.test.ts: N consecutive probe failures → heartbeat_failed×N + state='reconnecting'.
66-
- **2.2b** — PR TBD. Premise correction: plan §2.2 ¶2 premise ("one `autoAnnounceService` with per-protocol send adapters") is not supported by the code — same outcome shape as 2.2a. The two announce pipelines share exactly one mechanism: the cron-vs-interval arming skeleton (~25–30 LOC each). Everything an adapter would abstract (send call, channel identity, templating, gates, on-start semantics, follow-up burst, settings namespace, restart wiring) is genuinely protocol-specific. Reduced scope: extract a narrow `CronOrIntervalScheduler` leaf primitive (~120 LOC + 14-case fake-timer unit test) that owns arming only; both managers delegate to it. Full send-adapter service = 300+ LOC of new indirection to save the same 40 LOC — negative ROI. Files changed: `src/server/services/cronOrIntervalScheduler.ts` (NEW), `cronOrIntervalScheduler.test.ts` (NEW), `meshtasticManager.ts` (replace `announceInterval`+`announceCronJob` with `announceScheduler`; rewrite arming skeleton; on-start block untouched), `meshcoreManager.ts` (replace `autoAnnounceTimer`+`autoAnnounceCron` with `announceScheduler`; rewrite `startAutoAnnounce`/`stopAutoAnnounce`; advert timer preserved). Public method names/signatures unchanged. Behavior-preservation table (§6 of spec): all rows verified. Follow-ups: same arming skeleton copy-pasted in timer-triggers (both protocols) and distance-delete — candidates for a future CronOrIntervalScheduler adoption task (epic #3962 Phase 2).
66+
- **2.2b** — PR #4009 merged (CronOrIntervalScheduler leaf; both managers delegate arming; premise correction: ~40 LOC shared skeleton, adapter service = negative ROI). Ride-along: hotfix PR #4007 fixed main broken by #4004/#4002 merge race.
67+
- **2.2c** — Premise correction #3 (cross-manager duplication ≈0%; real find = Meshtastic matcher copy-pasted into its own test — now extracted so tests guard production; MeshCore intentionally separate). Extracted the ~130-line placeholder-DSL matcher (`{name}` / `{name:regex}` brace-parse → capture regex → param extraction) from `checkAutoResponder` in `meshtasticManager.ts` into `matchAutoResponderPattern()` + `AutoResponderMatch` interface in `src/utils/autoResponderUtils.ts`. `meshtasticManager.ts` now calls `matchAutoResponderPattern` (3-line loop); removed orphaned `normalizedText`. Retargeted `meshtasticManager.autoresponder-regex.test.ts` (621 lines) to import the production function — deleted local `extractParameters`+`testTriggerMatch` copy (lines 16–155), adapted all ~61 assertions to `{matched,params}`; added 1 explicit `{matched:false,params:{}}` edge-case assertion. MeshCore `checkAutoResponder` byte-for-byte unchanged. Full suite: success=true, 0 failed, 0 suitesFailed; typecheck clean; typecheck:tests 283 errors (at baseline); lint:ci exit 0.

0 commit comments

Comments
 (0)