From 9b28c277f0278dffb4c476a92921367c83043c2d Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Thu, 8 Oct 2026 20:00:10 +0200 Subject: [PATCH] fix(settings): keep shortcutOverrides out of the settings PUT The Shortcuts tab stores overrides in the per-device localStorage blob, and saveAppSettings() carries them over from the previous blob, but the strip before the PUT never removed them. SettingsUpdateSchema is .strict() and does not declare the key, so once a device had any override (even the empty {} that Reset leaves behind) every App Settings save got a 400 and no synced setting reached the server again, while the toast still read "Settings saved" (_apiPut resolves on a 400). Pre-existing, but #560 now points users at the Shortcuts tab to bind Close Session again, so it would be hit often. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/web/public/settings-ui.js | 7 +++++++ test/shortcut-registry-overlay.test.ts | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/web/public/settings-ui.js b/src/web/public/settings-ui.js index c36c5cba..1e223378 100644 --- a/src/web/public/settings-ui.js +++ b/src/web/public/settings-ui.js @@ -2775,6 +2775,13 @@ Object.assign(CodemanApp.prototype, { // .strict() schema — syncing it would push a desktop-shaped choice onto // devices that cannot render it at all. sessionLineageLines: _sll, + // Keyboard shortcut overrides are per-device (bindings follow the keyboard + // and the OS: Cmd on macOS, Ctrl elsewhere) and absent from the .strict() + // SettingsUpdateSchema. They used to ride along here, so the first + // Shortcuts change on a device (even a Reset, which leaves an empty {}) + // made EVERY later App Settings save a 400, and every synced key stopped + // reaching the server while the toast still said "Settings saved". + shortcutOverrides: _sco, ...serverSettings } = settings; let webhookError = ''; diff --git a/test/shortcut-registry-overlay.test.ts b/test/shortcut-registry-overlay.test.ts index d9f12735..fb48a419 100644 --- a/test/shortcut-registry-overlay.test.ts +++ b/test/shortcut-registry-overlay.test.ts @@ -69,6 +69,16 @@ describe('shortcut registry and overlay', () => { ); }); + it('keeps shortcutOverrides out of the settings PUT (the .strict() schema would 400 the whole save)', async () => { + // Overrides are per-device and live only in the localStorage blob. Before this + // strip, any device with an override (even an empty {} left by Reset) sent the + // key on every App Settings save, and the server refused the whole PUT, so no + // synced setting reached it again. + expect(settingsSource).toContain('shortcutOverrides: _sco,'); + const { SettingsUpdateSchema } = await import('../src/web/schemas.js'); + expect(SettingsUpdateSchema.safeParse({ shortcutOverrides: {} }).success).toBe(false); + }); + it('keeps the full help modal reachable now that Ctrl+? opens the registry overlay', () => { // The legacy #helpModal (full shortcut reference) lost its only opener when // Ctrl+? was rerouted to the overlay; the overlay footer must link to it.