From 9cb7017b00a4fb40aa0dfafe0ea72aeb80fcd43d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 06:20:47 +0000 Subject: [PATCH] docs(wiki): auto-sync pages + cover counts with docs --- ...ompression-Phase2-Named-Profiles-Design.md | 204 ++++++++ ...-Compression-Phase2-Named-Profiles-Plan.md | 465 ++++++++++++++++++ 2 files changed, 669 insertions(+) create mode 100644 2026-06-21-Compression-Phase2-Named-Profiles-Design.md create mode 100644 2026-06-21-Compression-Phase2-Named-Profiles-Plan.md diff --git a/2026-06-21-Compression-Phase2-Named-Profiles-Design.md b/2026-06-21-Compression-Phase2-Named-Profiles-Design.md new file mode 100644 index 0000000..1a88024 --- /dev/null +++ b/2026-06-21-Compression-Phase2-Named-Profiles-Design.md @@ -0,0 +1,204 @@ +> 🌍 [View in other languages](Languages) + + +# Compression Config Panel β€” Phase 2: Named Profiles + Active Selector + +**Status:** approved direction (2026-06-21), pending spec review +**Base branch:** `release/v3.8.32` (Phase 1 merged via #4432) +**Goal:** Let the operator pick which compression **profile** is globally active β€” the +panel-derived **Default** or one of the existing **named combos** β€” via a single +active-profile selector that writes `CompressionConfig.activeComboId`, and wire that +selection into the runtime so the chosen profile actually runs. Remove the now-duplicate +"master mode selector" and "Set as Default" controls so there is exactly one concept: +the active profile. + +Phase 1 (#4432) already built the `engines` map (the Default), `deriveDefaultPlan`, +`resolveCompressionPlan` (header/active-combo-aware), and persisted `activeComboId`. +Phase 3 (the `x-omniroute-compression` per-request header) is a separate later plan. + +--- + +## 1. Background β€” current state + +The named-combos infrastructure already exists; Phase 2 wires it up and removes +duplication. From the code map: + +- **`compression_combos` table** (migration 042) + **`src/lib/db/compressionCombos.ts`**: + full CRUD (`listCompressionCombos`, `createCompressionCombo`, `updateCompressionCombo`, + `deleteCompressionCombo`), routing-combo assignments, and an `is_default` flag + (`getDefaultCompressionCombo`/`setDefaultCompressionCombo`). A combo's `pipeline` is + `CompressionPipelineStep[]` (`{engine, intensity?, config?}`). +- **`CompressionCombosPageClient.tsx`** renders two blocks on `context/combos`: + - **`CompressionHub`** β€” today: a Token-Saver (master) toggle, a **Mode selector** + (Off/Lite/Standard/…/Stacked), a read-only active-pipeline display (read from the + 410-shim `/api/context/combos/default`), and reorder buttons. + - **`NamedCombosManager`** β€” full create/list/edit/delete of named combos + pipeline + editor + language packs + output mode + routing-combo assignments + a **"Set as + Default"** button (sets `is_default`). +- **`CompressionConfig.activeComboId`** is persisted (`/api/settings/compression`) but + **not wired into dispatch**: `resolveCompressionPlan` has the active-combo branch + (`ctx.combos[config.activeComboId]`), but Phase 1 only reaches it from inside + `deriveDefaultPlanFromConfig` (gated on `enginesExplicit`) and passes `combos: {}` + (empty), so the branch never fires. + +**The reconciliation (decided):** unify under the active profile. The active-profile +selector (writing `activeComboId`) is the single user-facing source of "which profile +runs". The `is_default` flag stays as a **backend-only** legacy detail (the fallback the +chatCore legacy block + `deriveEnginesMap` backfill read for installs that never opted +into the panel); it is no longer user-settable. The "Set as Default" button is removed. + +--- + +## 2. Architecture β€” dispatch + +### 2.1 Resolution model (precedence) + +``` +master off β†’ off + β†’ routing-combo override (config.comboOverrides[comboId]) β†’ that mode + β†’ ACTIVE PROFILE (config.activeComboId set + combo exists) β†’ that combo's pipeline [NEW] + β†’ auto-trigger (estimatedTokens β‰₯ autoTriggerTokens) β†’ autoTriggerMode + β†’ Default derived (enginesExplicit β†’ engines map; else legacy defaultMode) + β†’ off +``` + +The active profile is an **explicit operator choice**, so it resolves: +- **regardless of `enginesExplicit`** (setting `activeComboId` is itself an explicit + opt-in, even on a legacy install), and +- **above auto-trigger** (a manually-chosen profile wins over automatic escalation), +- but **below a per-routing-combo override** (a route-scoped override is more specific). + +### 2.2 Where it resolves (the Phase-1 gap) + +Phase 1's `resolveBasePlan` (in `strategySelector.ts`) does: master-off β†’ routing-combo +override β†’ auto-trigger β†’ `deriveDefaultPlanFromConfig`. The `activeComboId` branch lives +inside `resolveCompressionPlan`, which `deriveDefaultPlanFromConfig` only calls when +`enginesExplicit`, with `combos: {}`. So Phase 2 **lifts the active-profile resolution +into `resolveBasePlan`**, right after the routing-combo override and before auto-trigger: + +```ts +// resolveBasePlan, after checkComboOverride, before shouldAutoTrigger: +if (config.activeComboId && ctx.combos?.[config.activeComboId]) { + return { mode: "stacked", stackedPipeline: ctx.combos[config.activeComboId] }; +} +``` + +`ctx.combos` is `Record`. `selectCompressionPlan`/ +`selectCompressionStrategy` gain a `combos` param (threaded into `ctx`) so callers supply +it; existing callers that pass nothing default to `{}` (unchanged behavior). + +### 2.3 Loading the combos (Approach A β€” in chatCore) + +`chatCore.ts` already loads named combos for routing-combo assignment lookup. Phase 2 +extends that to build the full `combos` map once and pass it to `selectCompressionPlan`: + +```ts +const { listCompressionCombos } = await import("../../src/lib/db/compressionCombos.ts"); +const combos = Object.fromEntries(listCompressionCombos().map((c) => [c.id, c.pipeline])); +// …passed as the new combos arg to selectCompressionStrategy / selectCompressionPlan +``` + +When the active profile resolves to `stacked`, its pipeline reaches `applyCompressionAsync` +the same way Phase 1's engines-map override does β€” chatCore sets +`config.stackedPipeline = ctx.combos[activeComboId]` under the same guard +(`!compressionComboApplied && !config.compressionComboId`) so the operator's chosen profile +runs instead of the built-in default. `strategySelector` stays pure (no `src/lib/db` +import); only chatCore touches the DB. + +### 2.4 What stays untouched (legacy backend) + +`is_default`, `getDefaultCompressionCombo`, `setDefaultCompressionCombo`, +`setEngineInDefaultCombo`, the chatCore legacy default-combo block, and the +`deriveEnginesMap` backfill are **unchanged**. Setting `activeComboId` never writes +`is_default`. The active-profile path resolves *before* (and independently of) the legacy +default-combo fallback, so legacy installs that never set `activeComboId` keep their exact +behavior; an install that sets `activeComboId` runs that profile. + +--- + +## 3. UI + +No new API endpoints or DB migrations: `/api/settings/compression` already carries +`activeComboId`, and the combos CRUD API already exists. + +### 3.1 Active-profile selector β€” top of `CompressionHub` + +``` +Active profile: [ Default (from panel) β–Ό ] + β€’ Default (from panel) β†’ activeComboId = null + β€’ Standard Savings β†’ a named combo id + β€’ +``` + +On change β†’ `PUT /api/settings/compression { activeComboId }` (null for "Default"; +debounced/merge-patch like the panel's `save()`). Below it, a **read-only preview** of the +active profile's effective pipeline ("runs: rtk β†’ caveman β†’ …"): +- Default β†’ `deriveDefaultPlan(config.engines, config.enabled)` (the pure fn, client-side). +- Named combo β†’ that combo's pipeline. + +### 3.2 `CompressionHub` becomes a read-only overview β€” remove duplicates + +- **Remove** the **Mode selector** (Off/Lite/…/Stacked) β€” the mode is now derived; this is + the "master mode selector" the redesign removes. +- **Remove** the **Token Saver (master on/off) toggle** β€” it lives in the Panel + (`context/settings`), the single source since Phase 1. +- **Remove** the pipeline **reorder buttons** β€” ordering a named combo is done in the + combo editor (`NamedCombosManager`); the Default's order is auto-derived by + `stackPriority`. +- **Keep** only the active-profile selector + the read-only preview. + +### 3.3 `NamedCombosManager` β€” one change + +- **Remove** the **"Set as Default"** button (the active selector replaces it). +- **Keep** everything else: create/list/edit/delete, the pipeline editor (add/remove steps + + per-step intensity), language packs, output mode, routing-combo assignments. +- **Add** an **"● Active"** badge on the card whose id equals `activeComboId`. + +### 3.4 Navigation + +Sidebar order unchanged (Settings β†’ Combos β†’ per-engine pages β†’ Studio). + +--- + +## 4. Testing + +Both runners green (`test:unit` node + `test:vitest`); `typecheck:core` + `lint` clean; +`check:file-size` (rebaseline `CompressionHub.tsx`/`CompressionCombosPageClient.tsx` if +they grow, with a justification key). + +- **Resolver / dispatch (unit, node):** `selectCompressionStrategy`/`resolveBasePlan` with + `activeComboId` + `ctx.combos[id]` β†’ resolves to that combo's stacked pipeline, + **regardless of `enginesExplicit`**; `activeComboId` null β†’ Default derived; + `activeComboId` set but combo missing β†’ graceful fall-through. Precedence: + routing-combo override > active profile > auto-trigger > Default derived (one test per + boundary). +- **chatCore integration:** `activeComboId` set β†’ chatCore loads named combos β†’ the active + combo's pipeline runs via `applyCompressionAsync` (engineBreakdown reflects the combo's + engines), mirroring Phase 1's `derived-pipeline-integration` test. Plus: setting + `activeComboId` does **not** mutate `is_default` (`getDefaultCompressionCombo` + unchanged). +- **DB + API (unit):** `activeComboId` round-trips in `updateCompressionSettings`/ + `getCompressionSettings` and `PUT`/`GET /api/settings/compression`, including the + "switch combo β†’ null" path. +- **UI (vitest component):** `CompressionHub` renders the selector (Default + named combos), + changing it issues the right `PUT`, the preview reflects the selection, and the removed + controls (mode selector, master toggle, reorder) are **absent** (alignment, not masking). + `NamedCombosManager`: "Set as Default" gone; the active combo shows the "● Active" badge. + Honor the Phase-1 vitest gotchas (i18n renders English/keys β†’ assert on stable + strings/`data-testid`; new `@omniroute/...` imports don't resolve under vitest β†’ use + relative/re-export). +- **No regression:** `is_default`/`getDefaultCompressionCombo`/legacy chatCore block/ + backfill tests stay green and untouched. + +--- + +## 5. Non-goals (YAGNI) + +- **Phase 3** β€” the `x-omniroute-compression` per-request header β€” is a separate plan + (the resolver is already header-aware). +- No new compression engines; no new named-combo features (templates, sharing, import/ + export). +- No change to the combo CRUD API/DB (already shaped correctly). +- The `is_default` flag is kept (legacy backend fallback), not removed β€” removing it is a + larger migration out of scope here. +- The Compression Studio (analytics) is untouched. diff --git a/2026-06-21-Compression-Phase2-Named-Profiles-Plan.md b/2026-06-21-Compression-Phase2-Named-Profiles-Plan.md new file mode 100644 index 0000000..793c329 --- /dev/null +++ b/2026-06-21-Compression-Phase2-Named-Profiles-Plan.md @@ -0,0 +1,465 @@ +> 🌍 [View in other languages](Languages) + + +# Compression Phase 2 β€” Named Profiles + Active Selector Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Wire `CompressionConfig.activeComboId` into the runtime and give the operator a single active-profile selector (Default-from-panel | a named combo) on the Combos page, removing the now-duplicate "master mode selector" and "Set as Default" controls. + +**Architecture:** The resolver already has the model; Phase 2 (a) lifts the active-combo resolution into `resolveBasePlan` (so it applies regardless of `enginesExplicit`, above auto-trigger, below a routing-combo override), threading a `combos` map through `selectCompressionPlan`/`selectCompressionStrategy`; (b) `chatCore` loads named combos via `listCompressionCombos()` and passes them in, and guards the legacy default-combo block so it can't shadow an active profile; (c) the UI adds an active-profile selector + read-only preview to `CompressionHub` (removing the master toggle / mode selector / reorder) and removes "Set as Default" from `NamedCombosManager` (adding an "● Active" badge). No new API endpoints or DB migrations. + +**Tech Stack:** TypeScript, Next.js 16 App Router, SQLite (better-sqlite3), Node test runner + Vitest (component), React. + +**Base:** worktree `feat/compression-phase2-named-profiles` off `release/v3.8.32`. Spec: `docs/compression/2026-06-21-compression-phase2-named-profiles-design.md`. + +**Conventions:** unit tests run `node --import tsx/esm --test tests/unit/.test.ts`; component tests run `npx vitest run `. Each task ends by running the FULL compression suite (`node --import tsx/esm --test tests/unit/compression/*.test.ts`) + `npm run typecheck:core` before commit. Never `--no-verify`. Both UI files (`CompressionHub.tsx`, `CompressionCombosPageClient.tsx`) deliberately use **literal English strings, NOT `useTranslations`** β€” so vitest component tests can assert on those strings directly. + +--- + +## File Structure + +**Modify:** +- `open-sse/services/compression/strategySelector.ts` β€” thread a `combos` param through `resolveBasePlan`/`selectCompressionPlan`/`selectCompressionStrategy`/`getEffectiveMode`; resolve the active combo in `resolveBasePlan`; export `activeComboResolves()`. +- `open-sse/handlers/chatCore.ts` β€” load named combos, pass them to the two `selectCompression*` calls, guard the legacy default-combo block with `!activeComboResolves(...)`. +- `src/app/(dashboard)/dashboard/context/combos/CompressionHub.tsx` β€” remove master toggle / mode selector / reorder; add the active-profile selector + preview. +- `src/app/(dashboard)/dashboard/context/combos/CompressionCombosPageClient.tsx` β€” remove `setDefault` + "Set as Default" button; add the "● Active" badge from `activeComboId`. + +**Create (tests):** +- `tests/unit/compression/active-combo-dispatch.test.ts` +- `tests/unit/compression/active-combo-integration.test.ts` +- `tests/unit/ui/compressionHub-active-selector.test.tsx` +- `tests/unit/ui/namedCombos-active-badge.test.tsx` + +**Reference types:** `CompressionPipelineStep` = `{ engine: CompressionEngineId; intensity?: CavemanIntensity | RtkIntensity; config?: Record }` (in `open-sse/services/compression/types.ts`). `DerivedPlan` = `{ mode: string; stackedPipeline: Array<{ engine: string; intensity?: string }> }` (in `deriveDefaultPlan.ts`). + +--- + +## Task 1: Dispatch β€” resolve the active combo + thread `combos` + +**Files:** +- Modify: `open-sse/services/compression/strategySelector.ts` +- Test: `tests/unit/compression/active-combo-dispatch.test.ts` + +- [ ] **Step 1: Write the failing test** at `tests/unit/compression/active-combo-dispatch.test.ts` + +```ts +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { + selectCompressionStrategy, + selectCompressionPlan, + activeComboResolves, +} from "../../../open-sse/services/compression/strategySelector.ts"; +import { + DEFAULT_COMPRESSION_CONFIG, + type CompressionConfig, +} from "../../../open-sse/services/compression/types.ts"; + +const combos = { c1: [{ engine: "rtk", intensity: "standard" }, { engine: "caveman", intensity: "full" }] }; + +function cfg(overrides: Partial = {}): CompressionConfig { + return { ...DEFAULT_COMPRESSION_CONFIG, enabled: true, ...overrides }; +} + +describe("active named combo resolution (Phase 2)", () => { + it("activeComboId + combo present => that combo's stacked pipeline (regardless of enginesExplicit)", () => { + const config = cfg({ activeComboId: "c1", enginesExplicit: false }); + const plan = selectCompressionPlan(config, null, 0, undefined, undefined, combos); + assert.equal(plan.mode, "stacked"); + assert.deepEqual(plan.stackedPipeline, combos.c1); + }); + it("activeComboId null => falls through to derived default (not the combo)", () => { + const config = cfg({ activeComboId: null, enginesExplicit: true, engines: { rtk: { enabled: true } } }); + assert.equal(selectCompressionStrategy(config, null, 0, undefined, undefined, combos), "rtk"); + }); + it("activeComboId set but combo missing => graceful fall-through to default", () => { + const config = cfg({ activeComboId: "ghost", defaultMode: "lite", enginesExplicit: false }); + assert.equal(selectCompressionStrategy(config, null, 0, undefined, undefined, combos), "lite"); + }); + it("routing-combo override wins over the active profile", () => { + const config = cfg({ activeComboId: "c1", comboOverrides: { "my-combo": "off" } }); + assert.equal(selectCompressionStrategy(config, "my-combo", 0, undefined, undefined, combos), "off"); + }); + it("active profile wins over auto-trigger", () => { + const config = cfg({ activeComboId: "c1", autoTriggerTokens: 1000, autoTriggerMode: "aggressive" }); + assert.equal(selectCompressionStrategy(config, null, 5000, undefined, undefined, combos), "stacked"); + }); +}); + +describe("activeComboResolves", () => { + it("true only when activeComboId is set AND present in combos", () => { + assert.equal(activeComboResolves(cfg({ activeComboId: "c1" }), combos), true); + assert.equal(activeComboResolves(cfg({ activeComboId: "ghost" }), combos), false); + assert.equal(activeComboResolves(cfg({ activeComboId: null }), combos), false); + }); +}); +``` + +- [ ] **Step 2: Run β†’ FAIL** (`node --import tsx/esm --test tests/unit/compression/active-combo-dispatch.test.ts`) β€” `activeComboResolves` not exported / `combos` arg ignored. + +- [ ] **Step 3: Implement** in `strategySelector.ts`. Add the `CompressionPipelineStep` type import (it's already imported from `./types.ts` in most cases β€” confirm; if not, add it). Define the combos type alias near the top after imports: + +```ts +type NamedCombos = Record; +``` + +Change `resolveBasePlan` to accept `combos` and resolve the active combo after the routing-combo override, before auto-trigger: + +```ts +function resolveBasePlan( + config: CompressionConfig, + comboId: string | null, + estimatedTokens: number, + combos: NamedCombos = {} +): DerivedPlan { + if (!config.enabled) return { mode: "off", stackedPipeline: [] }; + + const comboMode = checkComboOverride(config, comboId); + if (comboMode) { + return resolveCompressionPlan(config, { comboId, combos }); + } + + // Active profile: an EXPLICIT operator choice. Resolves regardless of enginesExplicit and + // above auto-trigger (manual choice beats automatic escalation), but below a routing-combo + // override (route-scoped is more specific). + if (config.activeComboId && combos[config.activeComboId]) { + return { mode: "stacked", stackedPipeline: combos[config.activeComboId] }; + } + + if (shouldAutoTrigger(config, estimatedTokens)) { + const mode = config.autoTriggerMode ?? "lite"; + return mode === "stacked" + ? { mode, stackedPipeline: config.stackedPipeline ?? [] } + : { mode, stackedPipeline: [] }; + } + + return deriveDefaultPlanFromConfig(config, comboId, combos); +} +``` + +Add the `combos` param to `deriveDefaultPlanFromConfig` and pass it to its `resolveCompressionPlan` call: + +```ts +function deriveDefaultPlanFromConfig( + config: CompressionConfig, + comboId: string | null, + combos: NamedCombos = {} +): DerivedPlan { + if (config.enginesExplicit) { + return resolveCompressionPlan(config, { comboId, combos }); + } + const legacyMode = config.defaultMode; + if (legacyMode && legacyMode !== "off") { + return legacyMode === "stacked" + ? { mode: legacyMode, stackedPipeline: config.stackedPipeline ?? [] } + : { mode: legacyMode, stackedPipeline: [] }; + } + return { mode: "off", stackedPipeline: [] }; +} +``` + +Add `combos` as a trailing param to `getEffectiveMode`, `selectCompressionPlan`, `selectCompressionStrategy` and thread it down. Final signatures + bodies: + +```ts +export function getEffectiveMode( + config: CompressionConfig, + comboId: string | null, + estimatedTokens: number, + combos: NamedCombos = {} +): CompressionMode { + return resolveBasePlan(config, comboId, estimatedTokens, combos).mode as CompressionMode; +} + +export function selectCompressionPlan( + config: CompressionConfig, + comboId: string | null, + estimatedTokens: number, + body?: Record, + context?: CachingDetectionContext, + combos: NamedCombos = {} +): DerivedPlan { + const plan = resolveBasePlan(config, comboId, estimatedTokens, combos); + if (body) { + const ctx = detectCachingContext(body, context); + const cacheAware = getCacheAwareStrategy(plan.mode as CompressionMode, ctx); + return { ...plan, mode: cacheAware.strategy as CompressionMode }; + } + return plan; +} + +export function selectCompressionStrategy( + config: CompressionConfig, + comboId: string | null, + estimatedTokens: number, + body?: Record, + context?: CachingDetectionContext, + combos: NamedCombos = {} +): CompressionMode { + return selectCompressionPlan(config, comboId, estimatedTokens, body, context, combos).mode as CompressionMode; +} +``` + +Add the exported helper (near `enginesMapDerivesStackedPipeline`): + +```ts +/** + * True when the config has an active named-combo selection that exists in the supplied combos + * map. chatCore uses this to keep the legacy default-combo fallback from shadowing the + * operator's active profile (same class of bug as the #4023 web-cookie shadowing). + */ +export function activeComboResolves(config: CompressionConfig, combos: NamedCombos = {}): boolean { + return Boolean(config.activeComboId && combos[config.activeComboId]); +} +``` + +If `CompressionPipelineStep` is not already imported in `strategySelector.ts`, add it to the existing `import { ... } from "./types.ts"` (it is a type-only symbol). + +- [ ] **Step 4: Run β†’ PASS** + full compression suite + `npm run typecheck:core`. + +- [ ] **Step 5: Commit** + +```bash +git add open-sse/services/compression/strategySelector.ts tests/unit/compression/active-combo-dispatch.test.ts +git commit -m "feat(compression): resolve active named combo in dispatch (activeComboId)" +``` + +--- + +## Task 2: chatCore β€” load named combos + guard the legacy block + +**Files:** +- Modify: `open-sse/handlers/chatCore.ts` (compression dispatch block, ~lines 1428–1690) +- Test: `tests/unit/compression/active-combo-integration.test.ts` + +- [ ] **Step 1: Write the failing integration test** at `tests/unit/compression/active-combo-integration.test.ts` + +```ts +import { test, 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"; + +const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-active-combo-")); +const ORIGINAL = process.env.DATA_DIR; +process.env.DATA_DIR = TEST_DATA_DIR; + +const { getDbInstance, resetDbInstance } = await import("../../../src/lib/db/core.ts"); +const combosDb = await import("../../../src/lib/db/compressionCombos.ts"); +const { updateCompressionSettings } = await import("../../../src/lib/db/compression.ts"); +const { selectCompressionPlan } = await import("../../../open-sse/services/compression/strategySelector.ts"); +const { DEFAULT_COMPRESSION_CONFIG } = await import("../../../open-sse/services/compression/types.ts"); + +after(() => { + resetDbInstance(); + fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); + if (ORIGINAL === undefined) delete process.env.DATA_DIR; + else process.env.DATA_DIR = ORIGINAL; +}); + +test("an active named combo's pipeline is what selectCompressionPlan resolves, fed from the DB combos map", async () => { + resetDbInstance(); + getDbInstance(); + const created = combosDb.createCompressionCombo({ + name: "RTK only", + pipeline: [{ engine: "rtk", intensity: "standard" }], + }); + await updateCompressionSettings({ enabled: true, activeComboId: created.id }); + + // Mirror chatCore's load: build the combos map from the DB. + const combos = Object.fromEntries(combosDb.listCompressionCombos().map((c) => [c.id, c.pipeline])); + const config = { ...DEFAULT_COMPRESSION_CONFIG, enabled: true, activeComboId: created.id }; + const plan = selectCompressionPlan(config, null, 5000, undefined, undefined, combos); + assert.equal(plan.mode, "stacked"); + assert.deepEqual(plan.stackedPipeline, [{ engine: "rtk", intensity: "standard" }]); + + // Setting activeComboId did NOT change which combo is is_default (legacy untouched). + const def = combosDb.getDefaultCompressionCombo(); + assert.notEqual(def?.id, created.id); +}); +``` + +- [ ] **Step 2: Run β†’ FAIL** (the combos map is correct but confirms the wiring contract; if `createCompressionCombo`'s default seeding makes the new combo default, adjust the assertion to read the seeded `default-caveman` id β€” verify by reading `compressionCombos.ts`). Run: `node --import tsx/esm --test tests/unit/compression/active-combo-integration.test.ts`. + +- [ ] **Step 3: Implement** in `chatCore.ts`. In the compression block, AFTER `config` is loaded and BEFORE the first `selectCompressionStrategy` call (line ~1602), load the named combos once: + +```ts + let namedCombos: Record = {}; + try { + const { listCompressionCombos } = await import("../../src/lib/db/compressionCombos.ts"); + namedCombos = Object.fromEntries(listCompressionCombos().map((c) => [c.id, c.pipeline])); + } catch (err) { + log?.debug?.( + "COMPRESSION", + "Named combos load skipped: " + (err instanceof Error ? err.message : String(err)) + ); + } +``` + +Pass `namedCombos` as the 6th arg to BOTH `selectCompressionStrategy` (line ~1602) and `selectCompressionPlan` (line ~1668): + +```ts + const modeBeforeOutputTransform = selectCompressionStrategy( + config, + compressionComboKey, + estimatedTokens, + body as Record, + { provider, targetFormat, model: effectiveModel }, + namedCombos + ); +``` +```ts + const compressionPlan = selectCompressionPlan( + config, + compressionComboKey, + estimatedTokens, + compressionInputBody, + { provider, targetFormat, model: effectiveModel }, + namedCombos + ); +``` + +Guard the legacy default-combo block (the `if (modeBeforeOutputTransform === "stacked" && ... && !enginesMapDerivesStackedPipeline(config))` at line ~1609) so it does NOT fire when the active profile resolves β€” add `enginesMapDerivesStackedPipeline` is already imported; add `activeComboResolves` to the same import block (line ~1430) and add the guard term: + +```ts + const { + selectCompressionStrategy, + selectCompressionPlan, + enginesMapDerivesStackedPipeline, + activeComboResolves, // ← add + applyCompressionAsync, + resolveCacheAwareConfig, + } = await import("../services/compression/strategySelector.ts"); +``` +```ts + if ( + modeBeforeOutputTransform === "stacked" && + !compressionComboApplied && + !config.compressionComboId && + isBuiltinStackedPipeline(config.stackedPipeline) && + !enginesMapDerivesStackedPipeline(config) && + !activeComboResolves(config, namedCombos) // ← add: never shadow the active profile + ) { +``` + +The existing engines-map feed (line ~1680, `if (mode === "stacked" && compressionPlan.stackedPipeline.length > 0 && !compressionComboApplied && !config.compressionComboId)`) now ALSO feeds the active combo's pipeline into `config.stackedPipeline` (because `compressionPlan.stackedPipeline` is the active combo's when activeComboId resolves) β€” no change needed there. Confirm `CompressionPipelineStep` is imported in chatCore.ts (it is used by `config.stackedPipeline`); if not, add the type import. + +- [ ] **Step 4: Run β†’ PASS** + full compression suite + `npm run typecheck:core` + `npm run lint`. + +- [ ] **Step 5: Commit** + +```bash +git add open-sse/handlers/chatCore.ts tests/unit/compression/active-combo-integration.test.ts +git commit -m "feat(compression): chatCore loads named combos; legacy default combo can't shadow the active profile" +``` + +--- + +## Task 3: UI β€” active-profile selector in CompressionHub (remove duplicates) + +**Files:** +- Modify: `src/app/(dashboard)/dashboard/context/combos/CompressionHub.tsx` +- Test: `tests/unit/ui/compressionHub-active-selector.test.tsx` + +- [ ] **Step 1: Read the file first.** Note: it uses literal English strings (no `useTranslations`), `useState` for `settings`/`engines`/`combo`, a `saveSettings()` PUT to `/api/settings/compression`, a `MODES` array + a mode `` renders with options "Default (from panel)" and "RTK only". + 2. Changing it to `c1` issues a `PUT /api/settings/compression` whose body has `activeComboId === "c1"`. + 3. The preview (`data-testid="active-profile-preview"`) shows the combo's engines when a combo is active. + 4. There is NO mode `` + its handler, and `moveStep` + the reorder buttons. Keep the component's data load + error handling. + - **Add** `activeComboId` to the settings state/type; **fetch** the named combos list (`/api/context/combos`) on mount into a `combos` state. + - **Add** the active-profile `