Skip to content

Commit 1008055

Browse files
committed
chore(desktop): remove vestigial listMessages/replaceMessages IPC + DB
After chat_messages became the single source of truth for agent history (a5b737f), the legacy design_messages path had no live callers. This cleanup deletes: - preload: snapshots.listMessages / replaceMessages bindings - main IPC: snapshots:v1:list-messages / replace-messages handlers and channel entries (+ the dead parseDesignIdPayload / parseMessageList helpers they fed) - DB layer: listMessages / replaceMessages exports, rowToMessage mapper - tests: the DB-level describe block, the IPC describe block, the two schema-reject entries, and the leftover listMessages / replaceMessages mocks in store.test.ts What we intentionally leave alone: - design_messages table itself (DB schema unchanged — old data stays put) - duplicateDesign's design_messages copy loop (no-op for new designs since nobody writes to the table anymore; legacy designs still clone their historical rows cleanly) Both can be removed in a later DB schema migration; out of scope here. All 362 desktop tests pass; typecheck clean.
1 parent e11c923 commit 1008055

6 files changed

Lines changed: 2 additions & 255 deletions

File tree

apps/desktop/src/main/snapshots-db.test.ts

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* No Electron, no filesystem — just better-sqlite3 :memory:.
55
*/
66

7-
import { DesignMessageV1 } from '@open-codesign/shared';
87
import { describe, expect, it } from 'vitest';
98
import {
109
appendChatMessage,
@@ -17,10 +16,8 @@ import {
1716
initInMemoryDb,
1817
listChatMessages,
1918
listDesigns,
20-
listMessages,
2119
listSnapshots,
2220
renameDesign,
23-
replaceMessages,
2421
setDesignThumbnail,
2522
softDeleteDesign,
2623
updateChatToolCallStatus,
@@ -371,69 +368,11 @@ describe('softDeleteDesign + listDesigns filter', () => {
371368
});
372369
});
373370

374-
describe('design messages: replaceMessages + listMessages', () => {
375-
it('persists a message list keyed by design and ordinal', () => {
376-
const db = makeDb();
377-
const d = createDesign(db);
378-
replaceMessages(db, d.id, [
379-
{ role: 'user', content: 'first' },
380-
{ role: 'assistant', content: 'reply' },
381-
{ role: 'user', content: 'second' },
382-
]);
383-
const list = listMessages(db, d.id);
384-
expect(list).toHaveLength(3);
385-
expect(list[0]?.ordinal).toBe(0);
386-
expect(list[0]?.role).toBe('user');
387-
expect(list[1]?.role).toBe('assistant');
388-
expect(list[2]?.content).toBe('second');
389-
});
390-
391-
it('replaces (not appends) when called again', () => {
392-
const db = makeDb();
393-
const d = createDesign(db);
394-
replaceMessages(db, d.id, [{ role: 'user', content: 'a' }]);
395-
replaceMessages(db, d.id, [
396-
{ role: 'user', content: 'b' },
397-
{ role: 'assistant', content: 'c' },
398-
]);
399-
const list = listMessages(db, d.id);
400-
expect(list.map((m) => m.content)).toEqual(['b', 'c']);
401-
});
402-
403-
it('persists and loads system role messages (validates against DesignMessageV1)', () => {
404-
const db = makeDb();
405-
const d = createDesign(db);
406-
replaceMessages(db, d.id, [
407-
{ role: 'system', content: 'you are a designer' },
408-
{ role: 'user', content: 'make a hero' },
409-
{ role: 'assistant', content: 'done' },
410-
]);
411-
const list = listMessages(db, d.id);
412-
expect(list).toHaveLength(3);
413-
expect(list[0]?.role).toBe('system');
414-
for (const row of list) {
415-
expect(() => DesignMessageV1.parse(row)).not.toThrow();
416-
}
417-
});
418-
419-
it('cascades: deleting the design row removes its messages', () => {
420-
const db = makeDb();
421-
const d = createDesign(db);
422-
replaceMessages(db, d.id, [{ role: 'user', content: 'doomed' }]);
423-
db.prepare('DELETE FROM designs WHERE id = ?').run(d.id);
424-
expect(listMessages(db, d.id)).toEqual([]);
425-
});
426-
});
427-
428371
describe('duplicateDesign', () => {
429-
it('clones the design row, all messages, and all snapshots with parent rewiring', () => {
372+
it('clones the design row and all snapshots with parent rewiring', () => {
430373
const db = makeDb();
431374
const source = createDesign(db, 'Source');
432375
setDesignThumbnail(db, source.id, 'thumbnail preview');
433-
replaceMessages(db, source.id, [
434-
{ role: 'user', content: 'make a hero' },
435-
{ role: 'assistant', content: 'here you go' },
436-
]);
437376
const s1 = createSnapshot(db, {
438377
designId: source.id,
439378
parentId: null,
@@ -457,9 +396,6 @@ describe('duplicateDesign', () => {
457396
expect(cloned?.thumbnailText).toBe('thumbnail preview');
458397
expect(cloned?.id).not.toBe(source.id);
459398

460-
const clonedMessages = listMessages(db, cloned?.id ?? '');
461-
expect(clonedMessages.map((m) => m.content)).toEqual(['make a hero', 'here you go']);
462-
463399
const clonedSnaps = listSnapshots(db, cloned?.id ?? '');
464400
expect(clonedSnaps).toHaveLength(2);
465401
const clonedInitial = clonedSnaps.find((s) => s.type === 'initial');

apps/desktop/src/main/snapshots-db.ts

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import type {
2424
CommentUpdateInput,
2525
Design,
2626
DesignFile,
27-
DesignMessage,
2827
DesignSnapshot,
2928
SnapshotCreateInput,
3029
} from '@open-codesign/shared';
@@ -364,17 +363,6 @@ function rowToSnapshot(row: SnapshotRow): DesignSnapshot {
364363
};
365364
}
366365

367-
function rowToMessage(row: MessageRow): DesignMessage {
368-
return {
369-
schemaVersion: 1,
370-
designId: row.design_id,
371-
role: row.role as DesignMessage['role'],
372-
content: row.content,
373-
ordinal: row.ordinal,
374-
createdAt: row.created_at,
375-
};
376-
}
377-
378366
// ---------------------------------------------------------------------------
379367
// Designs
380368
// ---------------------------------------------------------------------------
@@ -550,48 +538,6 @@ export function deleteSnapshot(db: Database, id: string): void {
550538
db.prepare('DELETE FROM design_snapshots WHERE id = ?').run(id);
551539
}
552540

553-
// ---------------------------------------------------------------------------
554-
// Messages
555-
// ---------------------------------------------------------------------------
556-
557-
export function listMessages(db: Database, designId: string): DesignMessage[] {
558-
return (
559-
db
560-
.prepare('SELECT * FROM design_messages WHERE design_id = ? ORDER BY ordinal ASC')
561-
.all(designId) as MessageRow[]
562-
).map(rowToMessage);
563-
}
564-
565-
export interface MessageInput {
566-
role: 'user' | 'assistant' | 'system';
567-
content: string;
568-
}
569-
570-
/**
571-
* Replace the entire message list for a design atomically. We rewrite rather
572-
* than appending so the renderer's source-of-truth stays trivially in sync —
573-
* the chat list is small (< 200 entries) so a full rewrite is cheap and avoids
574-
* ordinal-conflict bugs across edits / cancels / retries.
575-
*/
576-
export function replaceMessages(
577-
db: Database,
578-
designId: string,
579-
messages: MessageInput[],
580-
): DesignMessage[] {
581-
const now = new Date().toISOString();
582-
const tx = db.transaction(() => {
583-
db.prepare('DELETE FROM design_messages WHERE design_id = ?').run(designId);
584-
const insert = db.prepare(
585-
'INSERT INTO design_messages (design_id, ordinal, role, content, created_at) VALUES (?, ?, ?, ?, ?)',
586-
);
587-
messages.forEach((m, i) => {
588-
insert.run(designId, i, m.role, m.content, now);
589-
});
590-
});
591-
tx();
592-
return listMessages(db, designId);
593-
}
594-
595541
// ---------------------------------------------------------------------------
596542
// Chat messages (Sidebar v2)
597543
// ---------------------------------------------------------------------------

apps/desktop/src/main/snapshots-ipc.test.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ describe('schemaVersion gating', () => {
361361
['snapshots:v1:set-thumbnail', { id: 'x', thumbnailText: null }],
362362
['snapshots:v1:soft-delete-design', { id: 'x' }],
363363
['snapshots:v1:duplicate-design', { id: 'x', name: 'n' }],
364-
['snapshots:v1:list-messages', { designId: 'd' }],
365-
['snapshots:v1:replace-messages', { designId: 'd', messages: [] }],
366364
[
367365
'snapshots:v1:create',
368366
{
@@ -656,37 +654,6 @@ describe('snapshots:v1:duplicate-design', () => {
656654
});
657655
});
658656

659-
describe('snapshots:v1:list-messages + replace-messages', () => {
660-
it('round-trips a message list', () => {
661-
const d = createDesign(db);
662-
call(
663-
'snapshots:v1:replace-messages',
664-
v1({
665-
designId: d.id,
666-
messages: [
667-
{ role: 'user', content: 'hello' },
668-
{ role: 'assistant', content: 'hi' },
669-
],
670-
}),
671-
);
672-
const list = call('snapshots:v1:list-messages', v1({ designId: d.id })) as Array<{
673-
role: string;
674-
content: string;
675-
}>;
676-
expect(list.map((m) => m.content)).toEqual(['hello', 'hi']);
677-
});
678-
679-
it('rejects malformed roles', () => {
680-
const d = createDesign(db);
681-
expect(() =>
682-
call(
683-
'snapshots:v1:replace-messages',
684-
v1({ designId: d.id, messages: [{ role: 'bot', content: 'x' }] }),
685-
),
686-
).toThrow();
687-
});
688-
});
689-
690657
describe('snapshots:v1:get-design', () => {
691658
it('returns the design row by id', () => {
692659
const d = createDesign(db, 'Lookup me');

apps/desktop/src/main/snapshots-ipc.ts

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,21 @@
99
* initSnapshotsDb().
1010
*/
1111

12-
import type {
13-
Design,
14-
DesignMessage,
15-
DesignSnapshot,
16-
SnapshotCreateInput,
17-
} from '@open-codesign/shared';
12+
import type { Design, DesignSnapshot, SnapshotCreateInput } from '@open-codesign/shared';
1813
import { CodesignError } from '@open-codesign/shared';
1914
import type BetterSqlite3 from 'better-sqlite3';
2015
import { ipcMain } from './electron-runtime';
2116
import { getLogger } from './logger';
2217
import {
23-
type MessageInput,
2418
createDesign,
2519
createSnapshot,
2620
deleteSnapshot,
2721
duplicateDesign,
2822
getDesign,
2923
getSnapshot,
3024
listDesigns,
31-
listMessages,
3225
listSnapshots,
3326
renameDesign,
34-
replaceMessages,
3527
setDesignThumbnail,
3628
softDeleteDesign,
3729
} from './snapshots-db';
@@ -345,27 +337,6 @@ export function registerSnapshotsIpc(db: Database): void {
345337
logger.info('design.duplicated', { sourceId: r['id'], newId: cloned.id });
346338
return cloned;
347339
});
348-
349-
ipcMain.handle('snapshots:v1:list-messages', (_e: unknown, raw: unknown): DesignMessage[] => {
350-
const id = parseDesignIdPayload(raw, 'list-messages');
351-
return runDb('list-messages', () => listMessages(db, id));
352-
});
353-
354-
ipcMain.handle('snapshots:v1:replace-messages', (_e: unknown, raw: unknown): DesignMessage[] => {
355-
if (typeof raw !== 'object' || raw === null) {
356-
throw new CodesignError(
357-
'snapshots:v1:replace-messages expects { designId, messages }',
358-
'IPC_BAD_INPUT',
359-
);
360-
}
361-
const r = raw as Record<string, unknown>;
362-
requireSchemaV1(r, 'snapshots:v1:replace-messages');
363-
if (typeof r['designId'] !== 'string' || r['designId'].trim().length === 0) {
364-
throw new CodesignError('designId must be a non-empty string', 'IPC_BAD_INPUT');
365-
}
366-
const messages = parseMessageList(r['messages']);
367-
return runDb('replace-messages', () => replaceMessages(db, r['designId'] as string, messages));
368-
});
369340
}
370341

371342
function parseIdPayload(raw: unknown, channel: string): string {
@@ -380,41 +351,6 @@ function parseIdPayload(raw: unknown, channel: string): string {
380351
return r['id'] as string;
381352
}
382353

383-
function parseDesignIdPayload(raw: unknown, channel: string): string {
384-
if (typeof raw !== 'object' || raw === null) {
385-
throw new CodesignError(`snapshots:v1:${channel} expects { designId }`, 'IPC_BAD_INPUT');
386-
}
387-
const r = raw as Record<string, unknown>;
388-
requireSchemaV1(r, `snapshots:v1:${channel}`);
389-
if (typeof r['designId'] !== 'string' || r['designId'].trim().length === 0) {
390-
throw new CodesignError('designId must be a non-empty string', 'IPC_BAD_INPUT');
391-
}
392-
return r['designId'] as string;
393-
}
394-
395-
function parseMessageList(raw: unknown): MessageInput[] {
396-
if (!Array.isArray(raw)) {
397-
throw new CodesignError('messages must be an array', 'IPC_BAD_INPUT');
398-
}
399-
const validRoles = ['user', 'assistant', 'system'] as const;
400-
return raw.map((entry, index) => {
401-
if (typeof entry !== 'object' || entry === null) {
402-
throw new CodesignError(`messages[${index}] must be an object`, 'IPC_BAD_INPUT');
403-
}
404-
const e = entry as Record<string, unknown>;
405-
if (!validRoles.includes(e['role'] as (typeof validRoles)[number])) {
406-
throw new CodesignError(
407-
`messages[${index}].role must be one of: ${validRoles.join(', ')}`,
408-
'IPC_BAD_INPUT',
409-
);
410-
}
411-
if (typeof e['content'] !== 'string') {
412-
throw new CodesignError(`messages[${index}].content must be a string`, 'IPC_BAD_INPUT');
413-
}
414-
return { role: e['role'] as MessageInput['role'], content: e['content'] as string };
415-
});
416-
}
417-
418354
/**
419355
* Stub channels installed when snapshots DB init fails at boot. Without these,
420356
* any renderer call to window.codesign.snapshots.* would surface as Electron's
@@ -432,8 +368,6 @@ export const SNAPSHOTS_CHANNELS_V1 = [
432368
'snapshots:v1:set-thumbnail',
433369
'snapshots:v1:soft-delete-design',
434370
'snapshots:v1:duplicate-design',
435-
'snapshots:v1:list-messages',
436-
'snapshots:v1:replace-messages',
437371
'snapshots:v1:list',
438372
'snapshots:v1:get',
439373
'snapshots:v1:create',

apps/desktop/src/preload/index.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type {
77
CommentRow,
88
CommentStatus,
99
Design,
10-
DesignMessage,
1110
DesignSnapshot,
1211
GeneratePayloadV1,
1312
LocalInputFile,
@@ -386,17 +385,6 @@ const api = {
386385
id,
387386
name,
388387
}) as Promise<Design>,
389-
listMessages: (designId: string) =>
390-
ipcRenderer.invoke('snapshots:v1:list-messages', {
391-
schemaVersion: 1,
392-
designId,
393-
}) as Promise<DesignMessage[]>,
394-
replaceMessages: (designId: string, messages: Array<{ role: string; content: string }>) =>
395-
ipcRenderer.invoke('snapshots:v1:replace-messages', {
396-
schemaVersion: 1,
397-
designId,
398-
messages,
399-
}) as Promise<DesignMessage[]>,
400388
list: (designId: string) =>
401389
ipcRenderer.invoke('snapshots:v1:list', { schemaVersion: 1, designId }) as Promise<
402390
DesignSnapshot[]

0 commit comments

Comments
 (0)