From cc7b3be2e2a49e3084f78d5885323e870f25a330 Mon Sep 17 00:00:00 2001 From: sKuhLight Date: Thu, 16 Jul 2026 13:20:22 +0200 Subject: [PATCH] deviceCache: match block-hi/param-hi in the walk reply pairing adaptRequest matched only fn + view + param-low, so a self-describe query for effectId>=128 (block-hi body[3]) or param>127 (param-hi body[5]) could pair with a stale reply for a different block/param. Match every echoed address byte (view f[6], block-lo f[8], block-hi f[9], param-lo f[10], param-hi f[11]) so the enhanced live-walk (reaching >=128 blocks + units) pairs correctly. All 22 suites pass; matching verified live via the codec harness. Phase-1 ForgeFX delta of META-27. --- server/package.json | 2 +- server/src/services/deviceCache.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/server/package.json b/server/package.json index 0552386..753d84b 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "forgefx-server", - "version": "0.6.26-beta", + "version": "0.6.28-beta", "private": true, "description": "ForgeFX — open HTTP API for Fractal devices. Node backend powered by fractal-midi.", "license": "MIT", diff --git a/server/src/services/deviceCache.ts b/server/src/services/deviceCache.ts index 6956392..6a9bf8f 100644 --- a/server/src/services/deviceCache.ts +++ b/server/src/services/deviceCache.ts @@ -85,8 +85,13 @@ const WALK_MAX_PARAM_ID = 127; function adaptRequest(transport: Transport, query: Uint8Array): Promise { const bytes = Array.from(query); const fn = bytes[5]; + // Match the fn AND every echoed address byte: view f[6], block-lo f[8] + block-hi + // f[9] (effectId>=128), param-lo f[10] + param-hi f[11] (id>127). Matching only + // view+param-lo (the old code) can pair a high-block/high-param query with a stale + // reply for a different block/param — a desync that turns a slowdown into a wedge. const isEcho = (f: readonly number[]): boolean => - isFractalHeaderFrame(f) && f[5] === fn && f[6] === bytes[6] && f[10] === bytes[10]; + isFractalHeaderFrame(f) && f[5] === fn && f[6] === bytes[6] + && f[8] === bytes[8] && f[9] === bytes[9] && f[10] === bytes[10] && f[11] === bytes[11]; return transport .request(bytes, { timeoutMs: 1000, quietMs: 20, match: (fs) => fs.some((f) => isEcho(f)) }) .then((frames) => {