Repository navigation
fix(db): merge partial cavemanOutputMode writes with the stored row - #15206
Merged
diegosouzapw merged 3 commits intoOct 2, 2026
Merged
diegosouzapw merged 3 commits into
diegosouzapw merged 3 commits into
Conversation
updateCompressionSettings stored each cavemanOutputMode write as the
whole JSON row, and getCompressionSettings fills missing fields with
defaults, so a partial write reset every field it left out.
{"autoClarity":false} also turned legacy output mode off and dropped
intensity to "lite". {"enabled":true} reset a stored autoClarity false
to true and intensity to "lite". The PUT /api/settings/compression
schema makes all three fields optional, so the route accepts both
bodies as-is.
The write path now reads the stored row inside the same transaction and
takes each missing or invalid field from it, the way
mergeLiteSettingsForWrite handles lite. An empty {} write leaves the
row unchanged. Like getCompressionSettings, the merge treats a non-text
(BLOB) stored row as absent.
Six regression cases in tests/unit/compression/db.test.ts cover both
reported writes, fields set back to their defaults, the empty write,
invalid fields, a partially populated stored row, and non-text,
unparseable, or null stored rows. A route test in
tests/unit/api/compression/compression-api.test.ts sends the reported
partial PUT body.
diegosouzapw
merged commit Oct 2, 2026
1caa0e1
into
diegosouzapw:release/v3.8.52
15 of 16 checks passed
Owner
|
Thanks @woodsonl — merged into |
woodsonl
added a commit
to woodsonl/OmniRoute
that referenced
this pull request
Oct 4, 2026
…ish pack Since diegosouzapw#15206 the store merges a partial cavemanOutputMode into its row, so the fake server does the same, and the Auto-Clarity case now requires the page to send only { autoClarity }. Add a case that turns a pack off and keeps English, and make the failed-read case a language save that starts from a failed first read.
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A partial
cavemanOutputModewrite now keeps the fields it leaves out.updateCompressionSettingsmerges what the caller sends with the stored row, inside the same transaction, the way it already mergeslite.Since #14551 merged on September 24, 2026, the output-styles path reads
autoClarity, so a write that resets it totrueturns the content bypass back on for chat requests. Resettingenabledturns the legacy caveman output mode off, and resettingintensitydrops it tolite.Any API client that sends part of the object hits this, through
PUT /api/settings/compressionorPUT /api/context/caveman/config, which re-exports the same handler (context/caveman/config/route.ts:1). The dashboard pages and the MCP tool send all three fields.The Defect
On the merge base,
updateCompressionSettingswrote every key exceptenginesandlitewithinsert.run(NAMESPACE, key, JSON.stringify(value))(src/lib/db/compression.ts:931). The request schema makes all threecavemanOutputModefields optional (cavemanOutputModeSchema,compressionConfigSchemas.ts:43-49), so a partial object reached the row as sent. On read,normalizeCavemanOutputModeConfigfills each missing field fromDEFAULT_CAVEMAN_OUTPUT_MODE_CONFIG:enabled: false,intensity: "lite",autoClarity: true(open-sse/services/compression/types.ts:463-467).Two writes show it:
{ enabled: true, intensity: "ultra", autoClarity: true }, then{"cavemanOutputMode":{"autoClarity":false}}: reads back{ enabled: false, intensity: "lite", autoClarity: false }.autoClarity: false, then{"cavemanOutputMode":{"enabled":true}}: reads backautoClarity: trueandintensity: "lite".What that changes at runtime:
resolveOutputStyleSelectionmapsenabledandintensityto theterse-prosestyle when no Output Styles are picked (outputStyles/backCompat.ts:25-26). The first write switches that style off.release/v3.8.52tip,chatCorepassesconfig.cavemanOutputMode?.autoClaritytoapplyOutputStyles(chatCore.ts:1708), which runs the content bypass unless the value isfalse(apply.ts:206). The second write switches the bypass back on.The Fix
normalizeCavemanOutputModeConfigtakes afallback(defaultDEFAULT_CAVEMAN_OUTPUT_MODE_CONFIG) and uses it field by field (compression.ts:117-131).mergeCavemanOutputModeForWrite(compression.ts:566-580) reads the stored row and normalizes the incoming value with that row as the fallback.cavemanOutputModethrough it (compression.ts:944-947), inside the samedb.transactionas the insert.Edge cases:
{}writes the stored values back unchanged.updateCompressionSettingsdirectly.getCompressionSettingsapplies on read (compression.ts:711-723).After a partial write, the row holds all three fields. The omitted ones keep what a read returned before the write: the stored value, or the default when the row had none. A field added to
CavemanOutputModeConfig(types.ts:78-82) needs a line in the normalizer too, or writes drop it.Related Work
autoClaritychanged elsewhere. A key the tab changes still goes out as its whole nested object; with this PR, a client can send just the nested field it changes.getCompressionSettings()and spreads all three fields (compressionTools.ts:432,:464-469). That read caches for 5 s per process (compression.ts:62,:902-907), so an MCP server running as its own process underomniroute --mcpcan write back values up to 5 s old.One Question for You
Nine more keys reset the same way. The write loop stores them whole, their normalizers fill omitted fields with defaults, and their schemas accept partial objects:
cavemanConfig,rtkConfig,codexResponsesConfig,languageConfig,aggressive,ultra,sessionDedup,ccr, andcontextBudget.headroomandcontextEditinghold one field each, so only an empty{}resets them. The MCP tool and/api/context/rtk/configmerge before they write (compressionTools.ts:453-462,context/rtk/config/route.ts:29-31), but a partialPUT /api/settings/compressionresets the fields it leaves out. Should I extend the merge to these keys in a follow-up PR?Testing
With the merge-base
compression.tsswapped back in and the new tests kept, 6 of the 7 new cases fail: the route case and 5 of the 6 DB cases. The seventh, "treats a non-text, unparseable, or null stored row as absent", passes there because the base code never reads the stored row; it guards the new read.Run the two files with:
On this branch:
tests/unit/compression/**, the compression and combos-default route tests,chatcore-compression-integration,test-model-compression-off-6240npm run test:vitestnpm run lintexits 2 on stale suppressions in files outside this change, as on the base; CI'snpm run lint:jsonpasses them through.tsconcompression.tsanddb.test.tsnpm run typecheck:coreskipscompression.ts; its one error,src/lib/services/cliproxyAccountHealth.ts:157, comes from the merge base and is fixed upstream.{}leaves the row as stored;{"intensity":"bogus"}gets a 400; the dashboard Auto-Clarity checkbox saves and survives a reload; a server restart keeps the merged rowcompression.tsagainst in-memory SQLite: all passThese runs used the merge base
d7be9fd528onrelease/v3.8.51, which predates the #14551 merge. The branch now includesrelease/v3.8.52atdbe703a000, merged with no conflicts, and the two changed test files pass 32/32 on the merged tree. The live checks ran on793fb5b7ed, whose production code matches the head; they differ only in the two test files.The 14 lane failures, all inherited from the merge base:
messages[0], which fix(compression): place output-style instruction in top-level system, not messages[0] #13383 moved out ofmessages[0](for these tests' bodies, to a trailing system message): 3 inchatcore-compression-integration.test.tsand 2 intest-model-compression-off-6240.test.ts.ReferenceError: providerResponse is not definedatchatCore.ts:706on the merge base, a regression from feat(providers): correlate X-CPA-TRACE-ID auth_index with usage history #14544 (the merge-base commit) that fix(sse): keep providerResponse in scope for failure usage (regression from #14544) #14599 fixed upstream inchatCore.ts.rtk,caveman, oraggressivein the resolved plan and getsession-dedupwithlite, or astackedplan.Upstream has since changed every file these tests live in: #14423 rewrote the 5
messages[0]assertions to expect a trailing system message, #14511 (which drains the #14496 base-reds) changed the five plan-test files, and #14551 also editedchatcore-compression-integration.test.ts.CI on this PR: the first run failed Docs Gates and the docs-counts test in unit shard 3/4 (
check-docs-counts-sync.test.ts) on stale 3.8.51 version strings inREADME.mdandllm.txt(#15100, since closed). Those strings read 3.8.52 atdbe703a000, and the docs-counts gate passes on the merged tree. Fast Quality Gates fails its mutation-test-coverage gate here and on #15203, a sibling PR on the same base, because 25 upstream unit tests across 8 modules are missing fromstryker.conf.json; all 8 modules sit outside this PR's diff.