From 10283f87e5572c858b11b2896057c66cec76fd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 9 Oct 2026 11:27:44 -0400 Subject: [PATCH] Q205: sprint records never tied to task progress are removed Co-Authored-By: Claude Opus 5.5 --- src/main/app/ipc-contracts/collaboration.ts | 2 -- src/main/app/ipc-handlers.ts | 4 --- .../database/migrations/110-drop-sprints.ts | 5 +++ src/main/database/schema.ts | 2 ++ src/main/services/snapshot-service.ts | 1 - src/main/services/sprint-service.ts | 36 ------------------- src/preload/index.ts | 2 -- src/shared/commands/collaboration.ts | 13 ------- src/shared/desktop-api.ts | 4 --- src/shared/domain.ts | 15 -------- src/shared/ipc.ts | 2 -- src/shared/snapshot-domain.ts | 2 -- tests/unit/domain-services.test.ts | 2 +- tests/unit/unused-records.test.ts | 8 +++++ 14 files changed, 16 insertions(+), 82 deletions(-) create mode 100644 src/main/database/migrations/110-drop-sprints.ts delete mode 100644 src/main/services/sprint-service.ts diff --git a/src/main/app/ipc-contracts/collaboration.ts b/src/main/app/ipc-contracts/collaboration.ts index e7e0a866..2ff4e856 100644 --- a/src/main/app/ipc-contracts/collaboration.ts +++ b/src/main/app/ipc-contracts/collaboration.ts @@ -26,8 +26,6 @@ export const collaborationContracts = { }) satisfies z.ZodType, deleteKnowledgeEntry: z.strictObject({ orgId: id, entryId: id, deletedByEmployeeId: id }) satisfies z.ZodType, listKnowledgeRevisions: z.strictObject({ orgId: id, entryId: id }) satisfies z.ZodType, - createSprint: z.strictObject({ orgId: id, name: z.string(), goal: z.string(), startDate: z.string(), endDate: z.string() }) satisfies z.ZodType, - deleteSprint: z.strictObject({ orgId: id, sprintId: id }) satisfies z.ZodType, createChatSpace: z.strictObject({ orgId: id, spaceType: z.enum(["company", "division", "department", "project", "pod", "ad_hoc", "direct"]), name: z.string(), topic: z.string(), visibility: z.enum(["workspace", "restricted"]), retentionPolicy: z.enum(["standard_365d", "restricted_365d"]).optional(), diff --git a/src/main/app/ipc-handlers.ts b/src/main/app/ipc-handlers.ts index b4b893da..3b602b9c 100644 --- a/src/main/app/ipc-handlers.ts +++ b/src/main/app/ipc-handlers.ts @@ -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 { SprintService } from "../services/sprint-service"; import { ComposioConnectionService } from "../services/composio/composio-connection-service"; import { ConnectorGrantsService } from "../services/composio/connector-grants"; import { approvalDrifts } from "../services/approval-drift"; @@ -90,7 +89,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 sprintService = new SprintService(deps.connection.db); const onboarding = new OnboardingService({ db: deps.connection.db, saveProviderCredential: (input) => providers.saveCredential(input), @@ -238,8 +236,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("createSprint", (input) => mutate(() => sprintService.create(input))); - handle("deleteSprint", (input) => mutate(() => sprintService.delete(input))); handle("createApproval", (input) => mutate(() => approvals.create(input))); handle("decideApproval", (input) => mutate(() => approvals.decide(input))); handle("syncEmployeeIdentity", (input) => mutate(() => identityAccess.syncEmployeeIdentity(input))); diff --git a/src/main/database/migrations/110-drop-sprints.ts b/src/main/database/migrations/110-drop-sprints.ts new file mode 100644 index 00000000..bcdf941a --- /dev/null +++ b/src/main/database/migrations/110-drop-sprints.ts @@ -0,0 +1,5 @@ +// Sprints were saved but never tracked against tasks; the records are removed rather than kept as a promise. +export const dropSprintsSchema = ` + drop index if exists idx_sprints_org; + drop table if exists sprints; +`; diff --git a/src/main/database/schema.ts b/src/main/database/schema.ts index 2ba28df1..6e27647f 100644 --- a/src/main/database/schema.ts +++ b/src/main/database/schema.ts @@ -108,6 +108,7 @@ 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"; +import { dropSprintsSchema } from "./migrations/110-drop-sprints"; interface Migration { version: number; @@ -225,6 +226,7 @@ const MIGRATIONS: Migration[] = [ { version: 107, name: "search_pending", sql: searchPendingSchema }, { version: 108, name: "drop_automation_rules", sql: dropAutomationRulesSchema }, { version: 109, name: "drop_meetings", sql: dropMeetingsSchema }, + { version: 110, name: "drop_sprints", sql: dropSprintsSchema }, ]; const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version; diff --git a/src/main/services/snapshot-service.ts b/src/main/services/snapshot-service.ts index d8747eaa..47116f25 100644 --- a/src/main/services/snapshot-service.ts +++ b/src/main/services/snapshot-service.ts @@ -151,7 +151,6 @@ export class SnapshotService { auditEvents: recentInOrg(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"), - 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"), }; diff --git a/src/main/services/sprint-service.ts b/src/main/services/sprint-service.ts deleted file mode 100644 index ff15919e..00000000 --- a/src/main/services/sprint-service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { DatabaseSync } from "node:sqlite"; -import type { CreateSprintInput, DeleteSprintInput } from "../../shared/commands"; -import { createId } from "../../shared/ids"; -import { AuditService } from "./audit-service"; - -export class SprintService { - private readonly audit: AuditService; - - constructor(private readonly db: DatabaseSync) { - this.audit = new AuditService(db); - } - - create(input: CreateSprintInput): string { - const id = createId("spr"); - const now = new Date().toISOString(); - this.db.prepare(` - insert into sprints(id, org_id, name, goal, status, start_date, end_date, velocity_points, completed_points, retrospective, created_at, updated_at) - values (?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, ?) - `).run(id, input.orgId, input.name.trim(), input.goal.trim(), "planned", input.startDate, input.endDate, "", now, now); - this.audit.record({ - orgId: input.orgId, - entityType: "sprint", - entityId: id, - actorType: "system", - actorId: null, - action: "created", - payload: { name: input.name.trim() }, - }); - return id; - } - - delete(input: DeleteSprintInput): void { - const result = this.db.prepare("delete from sprints where id = ? and org_id = ?").run(input.sprintId, input.orgId); - if (result.changes === 0) throw new Error("Sprint was not found."); - } -} diff --git a/src/preload/index.ts b/src/preload/index.ts index f3df18b0..dd82c653 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -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), - createSprint: (input) => invoke(IPC_CHANNELS.createSprint, input), - deleteSprint: (input) => invoke(IPC_CHANNELS.deleteSprint, input), createApproval: (input) => invoke(IPC_CHANNELS.createApproval, input), decideApproval: (input) => invoke(IPC_CHANNELS.decideApproval, input), syncEmployeeIdentity: (input) => invoke(IPC_CHANNELS.syncEmployeeIdentity, input), diff --git a/src/shared/commands/collaboration.ts b/src/shared/commands/collaboration.ts index 7e57b032..deff19f9 100644 --- a/src/shared/commands/collaboration.ts +++ b/src/shared/commands/collaboration.ts @@ -103,16 +103,3 @@ export interface DeleteKnowledgeEntryInput { entryId: string; deletedByEmployeeId: string; } - -export interface CreateSprintInput { - orgId: string; - name: string; - goal: string; - startDate: string; - endDate: string; -} - -export interface DeleteSprintInput { - orgId: string; - sprintId: string; -} diff --git a/src/shared/desktop-api.ts b/src/shared/desktop-api.ts index 3131b7e6..a4277743 100644 --- a/src/shared/desktop-api.ts +++ b/src/shared/desktop-api.ts @@ -19,8 +19,6 @@ import type { CreateApprovalInput, CreateDocumentInput, CreateGoalInput, - CreateSprintInput, - DeleteSprintInput, CreateKnowledgeEntryInput, UpdateKnowledgeEntryInput, DocumentVersionRecord, @@ -159,8 +157,6 @@ export interface DesktopApi { updateKnowledgeEntry(input: UpdateKnowledgeEntryInput): Promise>; listDocumentVersions(input: ListDocumentVersionsInput): Promise>; listKnowledgeRevisions(input: ListKnowledgeRevisionsInput): Promise>; - createSprint(input: CreateSprintInput): Promise>; - deleteSprint(input: DeleteSprintInput): Promise>; createApproval(input: CreateApprovalInput): Promise>; decideApproval(input: DecideApprovalInput): Promise>; syncEmployeeIdentity(input: SyncEmployeeIdentityInput): Promise>; diff --git a/src/shared/domain.ts b/src/shared/domain.ts index ac853da7..b01447e4 100644 --- a/src/shared/domain.ts +++ b/src/shared/domain.ts @@ -244,21 +244,6 @@ export interface ApprovalRecord { decidedAt: string | null; } -export interface SprintRecord { - id: string; - orgId: string; - name: string; - goal: string; - status: string; - startDate: string; - endDate: string; - velocityPoints: number; - completedPoints: number; - retrospective: string; - createdAt: string; - updatedAt: string; -} - export interface FileObjectRecord { id: string; orgId: string; diff --git a/src/shared/ipc.ts b/src/shared/ipc.ts index b5279e59..60f560a6 100644 --- a/src/shared/ipc.ts +++ b/src/shared/ipc.ts @@ -77,8 +77,6 @@ export const IPC_CHANNELS = { updateKnowledgeEntry: "agentcompany:update-knowledge-entry", listDocumentVersions: "agentcompany:list-document-versions", listKnowledgeRevisions: "agentcompany:list-knowledge-revisions", - createSprint: "agentcompany:create-sprint", - deleteSprint: "agentcompany:delete-sprint", listConnectorCatalog: "agentcompany:list-connector-catalog", connectComposioToolkit: "agentcompany:connect-composio-toolkit", disconnectComposioToolkit: "agentcompany:disconnect-composio-toolkit", diff --git a/src/shared/snapshot-domain.ts b/src/shared/snapshot-domain.ts index ac3f6c11..6447cf99 100644 --- a/src/shared/snapshot-domain.ts +++ b/src/shared/snapshot-domain.ts @@ -36,7 +36,6 @@ import type { ProviderRecord, ScheduledPostRecord, SkillDefinitionRecord, - SprintRecord, TaskRecord, TaskRunRecord, WorkspaceRecord, @@ -125,7 +124,6 @@ export interface AppSnapshot { auditEvents: AuditEventRecord[]; documents: DocumentRecord[]; knowledgeEntries: KnowledgeEntryRecord[]; - sprints: SprintRecord[]; connectorAccounts: ConnectorAccountRecord[]; connectorGrants: ConnectorGrantRecord[]; } diff --git a/tests/unit/domain-services.test.ts b/tests/unit/domain-services.test.ts index 0eec1fb7..cb97749d 100644 --- a/tests/unit/domain-services.test.ts +++ b/tests/unit/domain-services.test.ts @@ -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, 109]); + 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, 110]); connection.close(); }); diff --git a/tests/unit/unused-records.test.ts b/tests/unit/unused-records.test.ts index 3c801ad7..52681808 100644 --- a/tests/unit/unused-records.test.ts +++ b/tests/unit/unused-records.test.ts @@ -21,3 +21,11 @@ test("meeting records are gone: no table, no channels", () => { expect(tables.map((row) => row.name)).not.toContain("meetings"); expect(Object.keys(IPC_CHANNELS).filter((key) => /meeting/i.test(key))).toEqual([]); }); + +test("sprint 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("sprints"); + expect(Object.keys(IPC_CHANNELS).filter((key) => /sprint/i.test(key))).toEqual([]); +});