Q204: meeting records that were never held are removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9f44e0bdee
commit
8861aa4c30
14 files changed
+16
-85
No files matched your search
@@ -26,10 +26,6 @@ export const collaborationContracts = {
|
||||
}) satisfies z.ZodType<C.UpdateKnowledgeEntryInput>,
|
||||
deleteKnowledgeEntry: z.strictObject({ orgId: id, entryId: id, deletedByEmployeeId: id }) satisfies z.ZodType<C.DeleteKnowledgeEntryInput>,
|
||||
listKnowledgeRevisions: z.strictObject({ orgId: id, entryId: id }) satisfies z.ZodType<C.ListKnowledgeRevisionsInput>,
|
||||
createMeeting: z.strictObject({
|
||||
orgId: id, title: z.string(), meetingType: z.string(), scheduledAt: z.string().nullable(), durationMinutes: z.number(), agenda: z.string(),
|
||||
}) satisfies z.ZodType<C.CreateMeetingInput>,
|
||||
deleteMeeting: z.strictObject({ orgId: id, meetingId: id }) satisfies z.ZodType<C.DeleteMeetingInput>,
|
||||
createSprint: z.strictObject({ orgId: id, name: z.string(), goal: z.string(), startDate: z.string(), endDate: z.string() }) satisfies z.ZodType<C.CreateSprintInput>,
|
||||
deleteSprint: z.strictObject({ orgId: id, sprintId: id }) satisfies z.ZodType<C.DeleteSprintInput>,
|
||||
createChatSpace: z.strictObject({
|
||||
|
||||
@@ -23,7 +23,6 @@ import { OperationsService } from "../services/operations-service";
|
||||
import { TaskExecutionService } from "../services/task-execution-service";
|
||||
import { DocumentService } from "../services/document-service";
|
||||
import { KnowledgeService } from "../services/knowledge-service";
|
||||
import { MeetingService } from "../services/meeting-service";
|
||||
import { SprintService } from "../services/sprint-service";
|
||||
import { ComposioConnectionService } from "../services/composio/composio-connection-service";
|
||||
import { ConnectorGrantsService } from "../services/composio/connector-grants";
|
||||
@@ -91,7 +90,6 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
const operations = new OperationsService(deps.connection.db);
|
||||
const documents = new DocumentService(deps.connection.db);
|
||||
const knowledge = new KnowledgeService(deps.connection.db);
|
||||
const meetingService = new MeetingService(deps.connection.db);
|
||||
const sprintService = new SprintService(deps.connection.db);
|
||||
const onboarding = new OnboardingService({
|
||||
db: deps.connection.db,
|
||||
@@ -240,8 +238,6 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
revision: r.revision, topic: r.topic, category: r.category, content: r.content, importance: r.importance,
|
||||
changedByType: r.changed_by_type, changedById: r.changed_by_id, reason: r.reason, createdAt: r.created_at,
|
||||
}))));
|
||||
handle("createMeeting", (input) => mutate(() => meetingService.create(input)));
|
||||
handle("deleteMeeting", (input) => mutate(() => meetingService.delete(input)));
|
||||
handle("createSprint", (input) => mutate(() => sprintService.create(input)));
|
||||
handle("deleteSprint", (input) => mutate(() => sprintService.delete(input)));
|
||||
handle("createApproval", (input) => mutate(() => approvals.create(input)));
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Meetings were saved but never held; the records are removed rather than kept as a promise.
|
||||
export const dropMeetingsSchema = `
|
||||
drop index if exists idx_meetings_org;
|
||||
drop table if exists meetings;
|
||||
`;
|
||||
@@ -107,6 +107,7 @@ import { onboardingProgressSchema } from "./migrations/105-onboarding-progress";
|
||||
import { viewStateSchema } from "./migrations/106-view-state";
|
||||
import { searchPendingSchema } from "./migrations/107-search-pending";
|
||||
import { dropAutomationRulesSchema } from "./migrations/108-drop-automation-rules";
|
||||
import { dropMeetingsSchema } from "./migrations/109-drop-meetings";
|
||||
|
||||
interface Migration {
|
||||
version: number;
|
||||
@@ -223,6 +224,7 @@ const MIGRATIONS: Migration[] = [
|
||||
{ version: 106, name: "view_state", sql: viewStateSchema },
|
||||
{ version: 107, name: "search_pending", sql: searchPendingSchema },
|
||||
{ version: 108, name: "drop_automation_rules", sql: dropAutomationRulesSchema },
|
||||
{ version: 109, name: "drop_meetings", sql: dropMeetingsSchema },
|
||||
];
|
||||
|
||||
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import type { CreateMeetingInput, DeleteMeetingInput } from "../../shared/commands";
|
||||
import { createId } from "../../shared/ids";
|
||||
import { AuditService } from "./audit-service";
|
||||
|
||||
export class MeetingService {
|
||||
private readonly audit: AuditService;
|
||||
|
||||
constructor(private readonly db: DatabaseSync) {
|
||||
this.audit = new AuditService(db);
|
||||
}
|
||||
|
||||
create(input: CreateMeetingInput): string {
|
||||
const id = createId("mtg");
|
||||
const now = new Date().toISOString();
|
||||
this.db.prepare(`
|
||||
insert into meetings(id, org_id, title, meeting_type, status, scheduled_at, duration_minutes, agenda, notes, decisions, created_at, updated_at)
|
||||
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(id, input.orgId, input.title.trim(), input.meetingType, "scheduled", input.scheduledAt, input.durationMinutes, input.agenda.trim(), "", "", now, now);
|
||||
this.audit.record({
|
||||
orgId: input.orgId,
|
||||
entityType: "meeting",
|
||||
entityId: id,
|
||||
actorType: "system",
|
||||
actorId: null,
|
||||
action: "created",
|
||||
payload: { title: input.title.trim(), meetingType: input.meetingType },
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
delete(input: DeleteMeetingInput): void {
|
||||
const result = this.db.prepare("delete from meetings where id = ? and org_id = ?").run(input.meetingId, input.orgId);
|
||||
if (result.changes === 0) throw new Error("Meeting was not found.");
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,6 @@ export class SnapshotService {
|
||||
auditEvents: recentInOrg<AppSnapshot["auditEvents"][number]>(this.db, "audit_events", "created_at desc", 200, activeOrgId),
|
||||
documents: list(this.db, "documents", "updated_at desc"),
|
||||
knowledgeEntries: list(this.db, "knowledge_entries", "updated_at desc"),
|
||||
meetings: list(this.db, "meetings", "scheduled_at desc"),
|
||||
sprints: list(this.db, "sprints", "start_date desc"),
|
||||
connectorAccounts: list(this.db, "connector_accounts", "toolkit_slug asc"),
|
||||
connectorGrants: list(this.db, "agent_connector_grants", "toolkit_slug asc, created_at asc"),
|
||||
|
||||
@@ -44,8 +44,6 @@ const api: DesktopApi = {
|
||||
updateKnowledgeEntry: (input) => invoke(IPC_CHANNELS.updateKnowledgeEntry, input),
|
||||
listDocumentVersions: (input) => invoke(IPC_CHANNELS.listDocumentVersions, input),
|
||||
listKnowledgeRevisions: (input) => invoke(IPC_CHANNELS.listKnowledgeRevisions, input),
|
||||
createMeeting: (input) => invoke(IPC_CHANNELS.createMeeting, input),
|
||||
deleteMeeting: (input) => invoke(IPC_CHANNELS.deleteMeeting, input),
|
||||
createSprint: (input) => invoke(IPC_CHANNELS.createSprint, input),
|
||||
deleteSprint: (input) => invoke(IPC_CHANNELS.deleteSprint, input),
|
||||
createApproval: (input) => invoke(IPC_CHANNELS.createApproval, input),
|
||||
|
||||
@@ -104,20 +104,6 @@ export interface DeleteKnowledgeEntryInput {
|
||||
deletedByEmployeeId: string;
|
||||
}
|
||||
|
||||
export interface CreateMeetingInput {
|
||||
orgId: string;
|
||||
title: string;
|
||||
meetingType: string;
|
||||
scheduledAt: string | null;
|
||||
durationMinutes: number;
|
||||
agenda: string;
|
||||
}
|
||||
|
||||
export interface DeleteMeetingInput {
|
||||
orgId: string;
|
||||
meetingId: string;
|
||||
}
|
||||
|
||||
export interface CreateSprintInput {
|
||||
orgId: string;
|
||||
name: string;
|
||||
|
||||
@@ -19,9 +19,7 @@ import type {
|
||||
CreateApprovalInput,
|
||||
CreateDocumentInput,
|
||||
CreateGoalInput,
|
||||
CreateMeetingInput,
|
||||
CreateSprintInput,
|
||||
DeleteMeetingInput,
|
||||
DeleteSprintInput,
|
||||
CreateKnowledgeEntryInput,
|
||||
UpdateKnowledgeEntryInput,
|
||||
@@ -161,8 +159,6 @@ export interface DesktopApi {
|
||||
updateKnowledgeEntry(input: UpdateKnowledgeEntryInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
listDocumentVersions(input: ListDocumentVersionsInput): Promise<DesktopResult<DocumentVersionRecord[]>>;
|
||||
listKnowledgeRevisions(input: ListKnowledgeRevisionsInput): Promise<DesktopResult<KnowledgeRevisionRecord[]>>;
|
||||
createMeeting(input: CreateMeetingInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
deleteMeeting(input: DeleteMeetingInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
createSprint(input: CreateSprintInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
deleteSprint(input: DeleteSprintInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
createApproval(input: CreateApprovalInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
|
||||
@@ -244,21 +244,6 @@ export interface ApprovalRecord {
|
||||
decidedAt: string | null;
|
||||
}
|
||||
|
||||
export interface MeetingRecord {
|
||||
id: string;
|
||||
orgId: string;
|
||||
title: string;
|
||||
meetingType: string;
|
||||
status: string;
|
||||
scheduledAt: string | null;
|
||||
durationMinutes: number;
|
||||
agenda: string;
|
||||
notes: string;
|
||||
decisions: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface SprintRecord {
|
||||
id: string;
|
||||
orgId: string;
|
||||
|
||||
@@ -77,8 +77,6 @@ export const IPC_CHANNELS = {
|
||||
updateKnowledgeEntry: "agentcompany:update-knowledge-entry",
|
||||
listDocumentVersions: "agentcompany:list-document-versions",
|
||||
listKnowledgeRevisions: "agentcompany:list-knowledge-revisions",
|
||||
createMeeting: "agentcompany:create-meeting",
|
||||
deleteMeeting: "agentcompany:delete-meeting",
|
||||
createSprint: "agentcompany:create-sprint",
|
||||
deleteSprint: "agentcompany:delete-sprint",
|
||||
listConnectorCatalog: "agentcompany:list-connector-catalog",
|
||||
|
||||
@@ -28,7 +28,6 @@ import type {
|
||||
FileObjectRecord,
|
||||
GoalRecord,
|
||||
KnowledgeEntryRecord,
|
||||
MeetingRecord,
|
||||
MigrationReportRecord,
|
||||
OrganizationRecord,
|
||||
PolicyRuleRecord,
|
||||
@@ -126,7 +125,6 @@ export interface AppSnapshot {
|
||||
auditEvents: AuditEventRecord[];
|
||||
documents: DocumentRecord[];
|
||||
knowledgeEntries: KnowledgeEntryRecord[];
|
||||
meetings: MeetingRecord[];
|
||||
sprints: SprintRecord[];
|
||||
connectorAccounts: ConnectorAccountRecord[];
|
||||
connectorGrants: ConnectorGrantRecord[];
|
||||
|
||||
@@ -36,7 +36,7 @@ describe("domain services", () => {
|
||||
const connection = createConnection();
|
||||
|
||||
const rows = connection.db.prepare("select version from schema_migrations order by version asc").all() as Array<{ version: number }>;
|
||||
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108]);
|
||||
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109]);
|
||||
connection.close();
|
||||
});
|
||||
|
||||
|
||||
@@ -13,3 +13,11 @@ test("automation rules are gone: no table, no channels", () => {
|
||||
expect(tables.map((row) => row.name)).not.toContain("automation_rules");
|
||||
expect(Object.keys(IPC_CHANNELS).filter((key) => /automation/i.test(key))).toEqual([]);
|
||||
});
|
||||
|
||||
test("meeting records are gone: no table, no channels", () => {
|
||||
const connection = new DatabaseConnection(join(tmpdir(), `agentcompany-new-${randomUUID()}.db`));
|
||||
const tables = connection.db.prepare("select name from sqlite_master where type = 'table'").all() as Array<{ name: string }>;
|
||||
connection.close();
|
||||
expect(tables.map((row) => row.name)).not.toContain("meetings");
|
||||
expect(Object.keys(IPC_CHANNELS).filter((key) => /meeting/i.test(key))).toEqual([]);
|
||||
});
|
||||
Reference in new issue
Block a user