From c250512e14d4c0bddec79fc40d238178b3570cc3 Mon Sep 17 00:00:00 2001 From: protosphinx <133899485+protosphinx@users.noreply.github.com> Date: Tue, 1 Sep 2026 16:08:34 +0000 Subject: [PATCH] feat(match): add spanId and correlationId to VOLATILE_KEYS Both spanId and correlationId are observability/tracing IDs generated per-request. The fuzzy match tier already normalized them via ID_KEY, but normalizedMatch (one tier earlier) did not strip them, so two requests differing only in spanId or correlationId would fall through to the fuzzy tier unnecessarily. Adding them to VOLATILE_KEYS makes normalizedMatch consistent with the existing treatment of requestId and traceId. Four new test cases cover: spanId alone, correlationId alone, both keys combined with other volatile keys, and near-miss keys ('span', 'correlation') that must NOT be stripped. --- package-lock.json | 30 ---------------------------- src/match.ts | 2 ++ test/match.volatile.test.ts | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72054eb..5048feb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -633,9 +633,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -653,9 +650,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -673,9 +667,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -693,9 +684,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -713,9 +701,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -733,9 +718,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1616,9 +1598,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1640,9 +1619,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1664,9 +1640,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1688,9 +1661,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ diff --git a/src/match.ts b/src/match.ts index b12a7f3..06ddef7 100644 --- a/src/match.ts +++ b/src/match.ts @@ -44,6 +44,8 @@ const VOLATILE_KEYS = new Set([ "updatedAt", "requestId", "traceId", + "spanId", + "correlationId", ]); function stripVolatile(v: unknown): unknown { diff --git a/test/match.volatile.test.ts b/test/match.volatile.test.ts index 13e7026..69a67dc 100644 --- a/test/match.volatile.test.ts +++ b/test/match.volatile.test.ts @@ -118,4 +118,43 @@ describe("normalizedMatch: volatile key stripping", () => { const b = req(2, "ping", { traceId: "t9", requestId: "r9" }); expect(normalizedMatch(a, b)).toBe(true); }); + + it("strips 'spanId' so differing values still match", () => { + const a = req(1, "tools/call", { name: "search", spanId: "span-aaa" }); + const b = req(2, "tools/call", { name: "search", spanId: "span-zzz" }); + expect(exactMatch(a, b)).toBe(false); + expect(normalizedMatch(a, b)).toBe(true); + }); + + it("strips 'correlationId' so differing values still match", () => { + const a = req(1, "list_prs", { repo: "mcprec", correlationId: "corr-111" }); + const b = req(2, "list_prs", { repo: "mcprec", correlationId: "corr-999" }); + expect(exactMatch(a, b)).toBe(false); + expect(normalizedMatch(a, b)).toBe(true); + }); + + it("strips spanId and correlationId alongside other volatile keys", () => { + const a = req(1, "search", { + q: "is:open", + spanId: "s1", + correlationId: "c1", + traceId: "t1", + requestId: "r1", + }); + const b = req(2, "search", { + q: "is:open", + spanId: "s9", + correlationId: "c9", + traceId: "t9", + requestId: "r9", + }); + expect(exactMatch(a, b)).toBe(false); + expect(normalizedMatch(a, b)).toBe(true); + }); + + it("does not strip 'span' or 'correlation' (not exact key matches)", () => { + const a = req(1, "x", { span: "s1", correlation: "c1" }); + const b = req(2, "x", { span: "s9", correlation: "c9" }); + expect(normalizedMatch(a, b)).toBe(false); + }); });