From e5624444877d77ba2f7204e2beb7a41fae2752d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 8 Oct 2026 21:36:07 -0400 Subject: [PATCH] Q132: remove PR bot gates that no task completion or goal close consulted Co-Authored-By: Claude Opus 5.5 --- src/main/app/ipc-contracts/platform.ts | 1 - src/main/app/ipc-handlers.ts | 3 - .../migrations/095-drop-pr-bot-gates.ts | 7 +++ src/main/database/schema.ts | 2 + src/main/services/migration-service.ts | 2 - src/main/services/organization-seed.ts | 3 - src/main/services/sdlc-service.ts | 49 --------------- src/main/services/snapshot-service.ts | 3 - src/preload/index.ts | 1 - src/shared/commands/governance.ts | 6 -- src/shared/desktop-api.ts | 2 - src/shared/domain.ts | 1 - src/shared/ipc.ts | 1 - src/shared/sdlc-domain.ts | 14 ----- src/shared/snapshot-domain.ts | 2 - tests/unit/domain-services.test.ts | 3 +- tests/unit/migration-service.test.ts | 2 - tests/unit/sdlc-removal.test.ts | 25 ++++++++ tests/unit/sdlc-service.test.ts | 59 ------------------- 19 files changed, 35 insertions(+), 151 deletions(-) create mode 100644 src/main/database/migrations/095-drop-pr-bot-gates.ts delete mode 100644 src/main/services/sdlc-service.ts delete mode 100644 src/shared/sdlc-domain.ts create mode 100644 tests/unit/sdlc-removal.test.ts delete mode 100644 tests/unit/sdlc-service.test.ts diff --git a/src/main/app/ipc-contracts/platform.ts b/src/main/app/ipc-contracts/platform.ts index 9b2de054..599c6cfe 100644 --- a/src/main/app/ipc-contracts/platform.ts +++ b/src/main/app/ipc-contracts/platform.ts @@ -57,7 +57,6 @@ export const platformContracts = { resolveMigrationReconciliation: z.strictObject({ orgId: id, reconciliationItemId: id, employeeId: id }) satisfies z.ZodType, setFeatureFlag: z.strictObject({ orgId: id, flagId: id, enabled: z.boolean() }) satisfies z.ZodType, runChaosDrill: z.strictObject({ orgId: id, drillId: id }) satisfies z.ZodType, - setPrBotGate: z.strictObject({ orgId: id, gateId: id, enabled: z.boolean() }) satisfies z.ZodType, createComplianceExport: z.strictObject({ orgId: id, subjectType: z.enum(["employee", "agent"]), subjectId: id, exportType: z.enum(COMPLIANCE_EXPORT_TYPE), requestedByEmployeeId: id, }) satisfies z.ZodType, diff --git a/src/main/app/ipc-handlers.ts b/src/main/app/ipc-handlers.ts index 54a88e72..b496486b 100644 --- a/src/main/app/ipc-handlers.ts +++ b/src/main/app/ipc-handlers.ts @@ -19,7 +19,6 @@ import { ComplianceService } from "../services/compliance-service"; import { SearchService } from "../services/search-service"; import { IdentityAccessService } from "../services/identity-access-service"; import { OperationsService } from "../services/operations-service"; -import { SdlcService } from "../services/sdlc-service"; import { AuditService } from "../services/audit-service"; import { RollbackService } from "../services/rollback-service"; import { TaskExecutionService } from "../services/task-execution-service"; @@ -78,7 +77,6 @@ export function registerIpcHandlers(deps: IpcDeps): void { const search = new SearchService(deps.connection.db); const identityAccess = new IdentityAccessService(deps.connection.db); const operations = new OperationsService(deps.connection.db); - const sdlc = new SdlcService(deps.connection.db); const audit = new AuditService(deps.connection.db); const rollback = new RollbackService(deps.connection.db); const documents = new DocumentService(deps.connection.db); @@ -379,7 +377,6 @@ export function registerIpcHandlers(deps: IpcDeps): void { handle("resolveMigrationReconciliation", (input) => mutate(() => rollback.resolveReconciliationItem(input))); handle("setFeatureFlag", (input) => mutate(() => operations.setFeatureFlag(input))); handle("runChaosDrill", (input) => mutate(() => operations.runChaosDrill(input))); - handle("setPrBotGate", (input) => mutate(() => sdlc.setPrBotGate(input))); handle("createComplianceExport", (input) => mutate(() => compliance.createExport(input))); handle("createAuditReplay", (input) => mutate(() => audit.createThreadReplay(input))); handle("createLegalHold", (input) => mutate(() => compliance.createLegalHold(input))); diff --git a/src/main/database/migrations/095-drop-pr-bot-gates.ts b/src/main/database/migrations/095-drop-pr-bot-gates.ts new file mode 100644 index 00000000..cf7695f3 --- /dev/null +++ b/src/main/database/migrations/095-drop-pr-bot-gates.ts @@ -0,0 +1,7 @@ +// PR bot gates were switches that no task completion or goal close ever consulted, so turning one on blocked nothing. +export const dropPrBotGatesSchema = ` + drop table if exists pr_bot_gates; + + insert into schema_migrations(version, name, applied_at) + values (95, 'drop_pr_bot_gates', datetime('now')); +`; diff --git a/src/main/database/schema.ts b/src/main/database/schema.ts index e2c3a024..c9255ac2 100644 --- a/src/main/database/schema.ts +++ b/src/main/database/schema.ts @@ -93,6 +93,7 @@ import { requestPurposeSchema } from "./migrations/091-request-purpose"; import { runActionTargetsSchema } from "./migrations/092-run-action-targets"; import { taskEnqueueErrorSchema } from "./migrations/093-task-enqueue-error"; import { dropConfigCountEvalsSchema } from "./migrations/094-drop-config-count-evals"; +import { dropPrBotGatesSchema } from "./migrations/095-drop-pr-bot-gates"; interface Migration { version: number; @@ -195,6 +196,7 @@ const MIGRATIONS: Migration[] = [ { version: 92, name: "run_action_targets", sql: runActionTargetsSchema }, { version: 93, name: "task_enqueue_error", sql: taskEnqueueErrorSchema }, { version: 94, name: "drop_config_count_evals", sql: dropConfigCountEvalsSchema }, + { version: 95, name: "drop_pr_bot_gates", sql: dropPrBotGatesSchema }, ]; const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version; diff --git a/src/main/services/migration-service.ts b/src/main/services/migration-service.ts index 7707404a..b3cbd634 100644 --- a/src/main/services/migration-service.ts +++ b/src/main/services/migration-service.ts @@ -32,7 +32,6 @@ const LEGACY_TABLES = [ "operational_events", "chaos_drills", "chaos_drill_runs", - "pr_bot_gates", "chat_spaces", "chat_messages", "chat_threads", @@ -159,7 +158,6 @@ const RELATION_CHECKS: RelationCheck[] = [ { table: "chaos_drills", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" }, { table: "chaos_drill_runs", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" }, { table: "chaos_drill_runs", column: "drill_id", referencedTable: "chaos_drills", referencedColumn: "id", reason: "dangling_drill_id" }, - { table: "pr_bot_gates", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" }, { table: "chat_spaces", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" }, { table: "chat_messages", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" }, { table: "chat_messages", column: "space_id", referencedTable: "chat_spaces", referencedColumn: "id", reason: "dangling_space_id" }, diff --git a/src/main/services/organization-seed.ts b/src/main/services/organization-seed.ts index ef94d7a1..d4035a7c 100644 --- a/src/main/services/organization-seed.ts +++ b/src/main/services/organization-seed.ts @@ -7,7 +7,6 @@ import { PolicyService } from "./policy-service"; import { IdentityAccessService } from "./identity-access-service"; import { OrgGovernanceService } from "./org-governance-service"; import { OperationsService } from "./operations-service"; -import { SdlcService } from "./sdlc-service"; import { assertUsableMentionName } from "../../shared/mention-handles"; /** @@ -22,7 +21,6 @@ export function seedOrganization(db: DatabaseSync, input: CreateOrganizationInpu const identityAccess = new IdentityAccessService(db); const governance = new OrgGovernanceService(db); const operations = new OperationsService(db); - const sdlc = new SdlcService(db); assertUsableMentionName(input.employeeName); const orgId = createId("org"); @@ -50,7 +48,6 @@ export function seedOrganization(db: DatabaseSync, input: CreateOrganizationInpu identityAccess.seedEmployeeAccess(orgId, employeeId, now); governance.seedOrganizationGovernance(orgId, employeeId, now); operations.seedOrganizationOperations(orgId, now); - sdlc.seedOrganizationSdlc(orgId, now); for (const space of [ ["company", "general", "Company coordination", "workspace"], diff --git a/src/main/services/sdlc-service.ts b/src/main/services/sdlc-service.ts deleted file mode 100644 index c7728a66..00000000 --- a/src/main/services/sdlc-service.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { DatabaseSync } from "node:sqlite"; -import type { SetPrBotGateInput } from "../../shared/commands"; -import { PR_BOT_GATE_KEY } from "../../shared/domain"; -import { createId } from "../../shared/ids"; -import { AuditService } from "./audit-service"; - -const PR_BOT_GATES = [ - { key: PR_BOT_GATE_KEY.STATIC_CHECKS, label: "Static checks", gateType: "typecheck_lint_test" }, - { key: PR_BOT_GATE_KEY.MIGRATION_DIFF, label: "Migration diff", gateType: "schema_review" }, - { key: PR_BOT_GATE_KEY.POLICY_DIFF, label: "Policy diff", gateType: "policy_review" }, - { key: PR_BOT_GATE_KEY.HUMAN_APPROVAL, label: "Human approval", gateType: "approval_required" }, -] as const; - -export class SdlcService { - private readonly audit: AuditService; - - constructor(private readonly db: DatabaseSync) { - this.audit = new AuditService(db); - } - - seedOrganizationSdlc(orgId: string, now: string): void { - for (const gate of PR_BOT_GATES) { - this.db.prepare(` - insert into pr_bot_gates(id, org_id, key, label, gate_type, enabled, created_at, updated_at) - values (?, ?, ?, ?, ?, 1, ?, ?) - on conflict(org_id, key) do nothing - `).run(createId("pbg"), orgId, gate.key, gate.label, gate.gateType, now, now); - } - } - - setPrBotGate(input: SetPrBotGateInput): void { - const gate = this.db.prepare("select key from pr_bot_gates where id = ? and org_id = ?") - .get(input.gateId, input.orgId) as { key: string } | undefined; - if (!gate) throw new Error("PR bot gate was not found."); - const now = new Date().toISOString(); - const result = this.db.prepare("update pr_bot_gates set enabled = ?, updated_at = ? where id = ? and org_id = ?") - .run(input.enabled ? 1 : 0, now, input.gateId, input.orgId); - if (result.changes === 0) throw new Error("PR bot gate was not found."); - this.audit.record({ - orgId: input.orgId, - entityType: "pr_bot_gate", - entityId: input.gateId, - actorType: "system", - actorId: null, - action: "updated", - payload: { key: gate.key, enabled: input.enabled }, - }); - } -} diff --git a/src/main/services/snapshot-service.ts b/src/main/services/snapshot-service.ts index bc3fb64f..54c9e7d2 100644 --- a/src/main/services/snapshot-service.ts +++ b/src/main/services/snapshot-service.ts @@ -81,8 +81,6 @@ export class SnapshotService { .map((event) => ({ ...event, success: toBool(event.success) })); const chaosDrills = list(this.db, "chaos_drills", "key asc") .map((drill) => ({ ...drill, enabled: toBool(drill.enabled) })); - const prBotGates = list(this.db, "pr_bot_gates", "key asc") - .map((gate) => ({ ...gate, enabled: toBool(gate.enabled) })); const rollbackSnapshots = list(this.db, "rollback_snapshots", "created_at desc") .map((snapshot) => ({ ...snapshot, active: toBool(snapshot.active) })); @@ -162,7 +160,6 @@ export class SnapshotService { operationalEvents, chaosDrills, chaosDrillRuns: list(this.db, "chaos_drill_runs", "started_at desc"), - prBotGates, auditReplayRuns: list(this.db, "audit_replay_runs", "created_at desc"), auditEvents: recentList(this.db, "audit_events", "created_at desc", 200), documents: list(this.db, "documents", "updated_at desc"), diff --git a/src/preload/index.ts b/src/preload/index.ts index b45ff6fb..e6e8bd19 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -81,7 +81,6 @@ const api: DesktopApi = { resolveMigrationReconciliation: (input) => invoke(IPC_CHANNELS.resolveMigrationReconciliation, input), setFeatureFlag: (input) => invoke(IPC_CHANNELS.setFeatureFlag, input), runChaosDrill: (input) => invoke(IPC_CHANNELS.runChaosDrill, input), - setPrBotGate: (input) => invoke(IPC_CHANNELS.setPrBotGate, input), createComplianceExport: (input) => invoke(IPC_CHANNELS.createComplianceExport, input), createAuditReplay: (input) => invoke(IPC_CHANNELS.createAuditReplay, input), createLegalHold: (input) => invoke(IPC_CHANNELS.createLegalHold, input), diff --git a/src/shared/commands/governance.ts b/src/shared/commands/governance.ts index da877eff..5a72902c 100644 --- a/src/shared/commands/governance.ts +++ b/src/shared/commands/governance.ts @@ -83,9 +83,3 @@ export interface RunChaosDrillInput { orgId: string; drillId: string; } - -export interface SetPrBotGateInput { - orgId: string; - gateId: string; - enabled: boolean; -} diff --git a/src/shared/desktop-api.ts b/src/shared/desktop-api.ts index 1aa40416..5f80923e 100644 --- a/src/shared/desktop-api.ts +++ b/src/shared/desktop-api.ts @@ -83,7 +83,6 @@ import type { SetFilePinnedInput, SetFeatureFlagInput, SetLegalHoldStateInput, - SetPrBotGateInput, SetRollbackSnapshotActiveInput, SetRetentionFreezeStateInput, SetScheduledPostStateInput, @@ -190,7 +189,6 @@ export interface DesktopApi { resolveMigrationReconciliation(input: ResolveMigrationReconciliationInput): Promise>; setFeatureFlag(input: SetFeatureFlagInput): Promise>; runChaosDrill(input: RunChaosDrillInput): Promise>; - setPrBotGate(input: SetPrBotGateInput): Promise>; createComplianceExport(input: CreateComplianceExportInput): Promise>; createAuditReplay(input: CreateAuditReplayInput): Promise>; createLegalHold(input: CreateLegalHoldInput): Promise>; diff --git a/src/shared/domain.ts b/src/shared/domain.ts index 4cbd8b01..4f6422a1 100644 --- a/src/shared/domain.ts +++ b/src/shared/domain.ts @@ -11,7 +11,6 @@ export { PROVIDER_DATA_POLICY, PROVIDER_EXECUTION_MODE, type ProviderDataPolicy, export { PROVIDER_REQUEST_STATE, type ProviderRequestRecord, type ProviderRequestState, type ProviderStreamRendererEvent } from "./provider-request-domain"; export type { ProviderRateLimitObservationRecord, ProviderRateLimitStateRecord } from "./provider-runtime-domain"; export type { MigrationCutoverEventRecord, MigrationReconciliationItemRecord, RollbackSnapshotRecord } from "./rollback-domain"; -export { PR_BOT_GATE_KEY, type PrBotGateKey, type PrBotGateRecord } from "./sdlc-domain"; export { SEARCH_ENTITY_TYPES, type SearchEntityType, type SearchResultRecord } from "./search-domain"; export type { BrowserActionPolicyRecord, BrowserActionRecord, BrowserSessionContainerRecord, SocialAccountGrantRecord, SocialAccountRecord, SocialApprovalRouteRecord, SocialAuditRecord } from "./social-domain"; export type { AppSnapshot, UiLanguage, UiPreferences, UiTheme } from "./snapshot-domain"; diff --git a/src/shared/ipc.ts b/src/shared/ipc.ts index a8b10919..4e20e966 100644 --- a/src/shared/ipc.ts +++ b/src/shared/ipc.ts @@ -54,7 +54,6 @@ export const IPC_CHANNELS = { resolveMigrationReconciliation: "agentcompany:resolve-migration-reconciliation", setFeatureFlag: "agentcompany:set-feature-flag", runChaosDrill: "agentcompany:run-chaos-drill", - setPrBotGate: "agentcompany:set-pr-bot-gate", createComplianceExport: "agentcompany:create-compliance-export", createAuditReplay: "agentcompany:create-audit-replay", createLegalHold: "agentcompany:create-legal-hold", diff --git a/src/shared/sdlc-domain.ts b/src/shared/sdlc-domain.ts deleted file mode 100644 index 3e2b9b99..00000000 --- a/src/shared/sdlc-domain.ts +++ /dev/null @@ -1,14 +0,0 @@ -type ValueOf = T[keyof T]; - -export const PR_BOT_GATE_KEY = { - STATIC_CHECKS: "static_checks", - MIGRATION_DIFF: "migration_diff", - POLICY_DIFF: "policy_diff", - HUMAN_APPROVAL: "human_approval", -} as const; - -export type PrBotGateKey = ValueOf; - -export interface PrBotGateRecord { - id: string; orgId: string; key: PrBotGateKey; label: string; gateType: string; enabled: boolean; createdAt: string; updatedAt: string; -} diff --git a/src/shared/snapshot-domain.ts b/src/shared/snapshot-domain.ts index 1e560d29..a1601f56 100644 --- a/src/shared/snapshot-domain.ts +++ b/src/shared/snapshot-domain.ts @@ -49,7 +49,6 @@ import type { ProviderAuthProfileRecord, ProviderAuthSessionRecord } from "./pro import type { ProviderRequestRecord } from "./provider-request-domain"; import type { ProviderRateLimitObservationRecord, ProviderRateLimitStateRecord } from "./provider-runtime-domain"; import type { MigrationCutoverEventRecord, MigrationReconciliationItemRecord, RollbackSnapshotRecord } from "./rollback-domain"; -import type { PrBotGateRecord } from "./sdlc-domain"; import type { BrowserActionPolicyRecord, BrowserActionRecord, @@ -139,7 +138,6 @@ export interface AppSnapshot { operationalEvents: OperationalEventRecord[]; chaosDrills: ChaosDrillRecord[]; chaosDrillRuns: ChaosDrillRunRecord[]; - prBotGates: PrBotGateRecord[]; auditReplayRuns: AuditReplayRunRecord[]; auditEvents: AuditEventRecord[]; documents: DocumentRecord[]; diff --git a/tests/unit/domain-services.test.ts b/tests/unit/domain-services.test.ts index 18456cd1..8a7a0c27 100644 --- a/tests/unit/domain-services.test.ts +++ b/tests/unit/domain-services.test.ts @@ -35,7 +35,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]); + 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]); connection.close(); }); @@ -63,7 +63,6 @@ describe("domain services", () => { expect(state.featureFlags.map((flag) => flag.key)).toContain("chaos_drills"); expect(state.sloTargets.map((target) => target.signalKey)).toContain("provider_request_success"); expect(state.chaosDrills.map((drill) => drill.scenarioKey)).toContain("approval_backlog_gate"); - expect(state.prBotGates.map((gate) => gate.key)).toContain("static_checks"); expect(state.chatSpaces.map((space) => space.name)).toContain("board"); expect(state.policyRules[0]?.action).toBe("task_create"); expect(state.auditEvents[0]?.action).toBe("created"); diff --git a/tests/unit/migration-service.test.ts b/tests/unit/migration-service.test.ts index d424a34c..288cc8ba 100644 --- a/tests/unit/migration-service.test.ts +++ b/tests/unit/migration-service.test.ts @@ -132,7 +132,6 @@ describe("migration service", () => { create table operational_events(id text primary key, org_id text); create table chaos_drills(id text primary key, org_id text); create table chaos_drill_runs(id text primary key, org_id text, drill_id text); - create table pr_bot_gates(id text primary key, org_id text); create table chat_spaces(id text primary key, org_id text); create table chat_messages(id text primary key, org_id text, space_id text, thread_id text); create table chat_threads(id text primary key, org_id text, space_id text, root_message_id text); @@ -205,7 +204,6 @@ describe("migration service", () => { insert into operational_events(id, org_id) values ('op-1', 'org-1'); insert into chaos_drills(id, org_id) values ('drill-1', 'org-1'); insert into chaos_drill_runs(id, org_id, drill_id) values ('drill-run-1', 'org-1', 'drill-1'); - insert into pr_bot_gates(id, org_id) values ('gate-1', 'org-1'); insert into chat_threads(id, org_id, space_id, root_message_id) values ('thread-1', 'org-1', 'space-1', null); insert into chat_messages(id, org_id, space_id, thread_id) values ('msg-1', 'org-1', 'space-1', 'thread-1'); insert into chat_memberships(space_id, principal_type, principal_id) values ('space-1', 'employee', 'emp-1'); diff --git a/tests/unit/sdlc-removal.test.ts b/tests/unit/sdlc-removal.test.ts new file mode 100644 index 00000000..be9b7522 --- /dev/null +++ b/tests/unit/sdlc-removal.test.ts @@ -0,0 +1,25 @@ +import { randomUUID } from "node:crypto"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { describe, expect, test } from "vitest"; +import { DatabaseConnection } from "../../src/main/database/connection"; +import { OrganizationService } from "../../src/main/services/organization-service"; +import { IPC_CHANNELS } from "../../src/shared/ipc"; + +describe("SDLC controls", () => { + test("offer no review, eval or PR gate that passes or blocks without checking real work", () => { + const connection = new DatabaseConnection(join(tmpdir(), `agentcompany-new-sdlc-${randomUUID()}.db`)); + connection.transaction(() => { + new OrganizationService(connection.db).createOrganization({ + organizationName: "Acme Operations", employeeName: "Dana Lee", mission: "Coordinate agents.", goal: "Ship a verified control plane.", + }); + }); + const tables = connection.db.prepare(` + select name from sqlite_master + where type = 'table' and name in ('sdlc_review_agents', 'sdlc_review_runs', 'eval_suites', 'eval_cases', 'eval_runs', 'pr_bot_gates') + `).all(); + expect(tables).toEqual([]); + expect(Object.keys(IPC_CHANNELS).filter((name) => ["runSdlcReview", "runEvalSuite", "setPrBotGate"].includes(name))).toEqual([]); + connection.close(); + }); +}); diff --git a/tests/unit/sdlc-service.test.ts b/tests/unit/sdlc-service.test.ts deleted file mode 100644 index 17e896b2..00000000 --- a/tests/unit/sdlc-service.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { randomUUID } from "node:crypto"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { describe, expect, test } from "vitest"; -import { DatabaseConnection } from "../../src/main/database/connection"; -import { OrganizationService } from "../../src/main/services/organization-service"; -import { SdlcService } from "../../src/main/services/sdlc-service"; -import { SnapshotService } from "../../src/main/services/snapshot-service"; -import { IPC_CHANNELS } from "../../src/shared/ipc"; - -function createConnection(): DatabaseConnection { - return new DatabaseConnection(join(tmpdir(), `agentcompany-new-sdlc-${randomUUID()}.db`)); -} - -function createOrganization(organization: OrganizationService): string { - return organization.createOrganization({ - organizationName: "Acme Operations", - employeeName: "Dana Lee", - mission: "Coordinate agents with auditable approvals.", - goal: "Ship a verified control plane.", - }); -} - -describe("SDLC service", () => { - test("offers no review or eval whose pass only counts configuration rows", () => { - const connection = createConnection(); - connection.transaction(() => { - createOrganization(new OrganizationService(connection.db)); - }); - const tables = connection.db.prepare(` - select name from sqlite_master where type = 'table' and name in ('sdlc_review_agents', 'sdlc_review_runs', 'eval_suites', 'eval_cases', 'eval_runs') - `).all(); - expect(tables).toEqual([]); - expect(Object.keys(IPC_CHANNELS).filter((name) => name === "runSdlcReview" || name === "runEvalSuite")).toEqual([]); - connection.close(); - }); - - test("seeds PR bot gates and toggles them", () => { - const connection = createConnection(); - const organization = new OrganizationService(connection.db); - const sdlc = new SdlcService(connection.db); - const snapshot = new SnapshotService(connection.db); - let orgId = ""; - let gateId = ""; - - connection.transaction(() => { - orgId = createOrganization(organization); - gateId = (connection.db.prepare("select id from pr_bot_gates where org_id = ? and key = ?") - .get(orgId, "static_checks") as { id: string }).id; - sdlc.setPrBotGate({ orgId, gateId, enabled: false }); - }); - - const state = snapshot.load(); - expect(state.prBotGates.map((gate) => gate.key).sort()).toEqual(["human_approval", "migration_diff", "policy_diff", "static_checks"]); - expect(state.prBotGates.find((gate) => gate.id === gateId)?.enabled).toBe(false); - expect(state.auditEvents.some((event) => event.entityType === "pr_bot_gate" && event.action === "updated")).toBe(true); - connection.close(); - }); -});