From aed2a5024c6fb9ccda81b0a87ee3a3f6c5a8cddd Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:12:27 -0300 Subject: [PATCH] feat(compression): persist RTK grouping config (unlock R5 enableGrouping) (#4207) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RTK R5 grouping strategy (collapse near-equivalent consecutive lines) is read by the engine (config.enableGrouping / groupingThreshold) but was unreachable in production: the rtkConfigSchema (.strict()) rejected the two fields on write and normalizeRtkConfig dropped them on read, so they could never be anything but undefined. Add enableGrouping (bool) + groupingThreshold (int, 2..100, default 3) to DEFAULT_RTK_CONFIG, normalizeRtkConfig, and rtkConfigSchema so the feature is actually settable and survives the DB round-trip. Default stays OFF — no behavior change for existing configs. Part of the compression "100% functional" program (audit follow-up). --- open-sse/services/compression/types.ts | 2 + src/lib/db/compression.ts | 10 +++ .../validation/compressionConfigSchemas.ts | 2 + .../compression/rtk-grouping-config.test.ts | 63 +++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 tests/unit/compression/rtk-grouping-config.test.ts diff --git a/open-sse/services/compression/types.ts b/open-sse/services/compression/types.ts index e3eebad8bc..0a84874976 100644 --- a/open-sse/services/compression/types.ts +++ b/open-sse/services/compression/types.ts @@ -230,6 +230,8 @@ export const DEFAULT_RTK_CONFIG: RtkConfig = { trustProjectFilters: false, rawOutputRetention: "never", rawOutputMaxBytes: 1_048_576, + enableGrouping: false, + groupingThreshold: 3, }; export const DEFAULT_COMPRESSION_LANGUAGE_CONFIG: CompressionLanguageConfig = { diff --git a/src/lib/db/compression.ts b/src/lib/db/compression.ts index 8e19fb9bc6..5b8ed98af4 100644 --- a/src/lib/db/compression.ts +++ b/src/lib/db/compression.ts @@ -171,6 +171,16 @@ function normalizeRtkConfig(value: unknown): RtkConfig { 1024, 10_000_000 ), + enableGrouping: + typeof record.enableGrouping === "boolean" + ? record.enableGrouping + : (DEFAULT_RTK_CONFIG.enableGrouping ?? false), + groupingThreshold: boundedInt( + record.groupingThreshold, + DEFAULT_RTK_CONFIG.groupingThreshold ?? 3, + 2, + 100 + ), }; } diff --git a/src/shared/validation/compressionConfigSchemas.ts b/src/shared/validation/compressionConfigSchemas.ts index d2fb09d8b4..5d7bbe6bdb 100644 --- a/src/shared/validation/compressionConfigSchemas.ts +++ b/src/shared/validation/compressionConfigSchemas.ts @@ -49,6 +49,8 @@ export const rtkConfigSchema = z trustProjectFilters: z.boolean().optional(), rawOutputRetention: rtkRawOutputRetentionSchema.optional(), rawOutputMaxBytes: z.number().int().min(1024).max(10_000_000).optional(), + enableGrouping: z.boolean().optional(), + groupingThreshold: z.number().int().min(2).max(100).optional(), }) .strict(); diff --git a/tests/unit/compression/rtk-grouping-config.test.ts b/tests/unit/compression/rtk-grouping-config.test.ts new file mode 100644 index 0000000000..6a65b1b6e3 --- /dev/null +++ b/tests/unit/compression/rtk-grouping-config.test.ts @@ -0,0 +1,63 @@ +import { describe, it, beforeEach, afterEach, after } from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +import { rtkConfigSchema } from "../../../src/shared/validation/compressionConfigSchemas.ts"; +import { DEFAULT_RTK_CONFIG } from "../../../open-sse/services/compression/types.ts"; + +// The RTK R5 grouping feature is read by the engine (config.enableGrouping / groupingThreshold) +// but was unreachable in production: the Zod schema (.strict()) rejected the two fields on write +// and normalizeRtkConfig dropped them on read. This proves both gates now let them through. + +const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-rtk-grouping-")); +const ORIGINAL_DATA_DIR = process.env.DATA_DIR; +process.env.DATA_DIR = TEST_DATA_DIR; + +const core = await import("../../../src/lib/db/core.ts"); +const { getCompressionSettings, updateCompressionSettings } = await import( + "../../../src/lib/db/compression.ts" +); + +describe("RTK grouping config persistence (R5)", () => { + beforeEach(() => { + core.resetDbInstance(); + fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); + fs.mkdirSync(TEST_DATA_DIR, { recursive: true }); + }); + + afterEach(() => { + core.resetDbInstance(); + }); + + after(() => { + core.resetDbInstance(); + fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); + if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR; + else process.env.DATA_DIR = ORIGINAL_DATA_DIR; + }); + + it("accepts enableGrouping / groupingThreshold on the write schema", () => { + assert.equal( + rtkConfigSchema.safeParse({ enableGrouping: true, groupingThreshold: 5 }).success, + true + ); + // groupingThreshold below the minimum run length (2) is rejected. + assert.equal(rtkConfigSchema.safeParse({ groupingThreshold: 1 }).success, false); + }); + + it("preserves enableGrouping / groupingThreshold through a DB round-trip", async () => { + const settings = await updateCompressionSettings({ + rtkConfig: { ...DEFAULT_RTK_CONFIG, enableGrouping: true, groupingThreshold: 7 }, + }); + assert.equal(settings.rtkConfig.enableGrouping, true); + assert.equal(settings.rtkConfig.groupingThreshold, 7); + + // Survives a fresh read (not just the write-path return value). + core.resetDbInstance(); + const reread = await getCompressionSettings(); + assert.equal(reread.rtkConfig.enableGrouping, true); + assert.equal(reread.rtkConfig.groupingThreshold, 7); + }); +});