Q132: remove PR bot gates that no task completion or goal close consulted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fe45cd4d46
commit
e562444487
19 files changed
+35
-151
No files matched your search
@@ -57,7 +57,6 @@ export const platformContracts = {
|
||||
resolveMigrationReconciliation: z.strictObject({ orgId: id, reconciliationItemId: id, employeeId: id }) satisfies z.ZodType<C.ResolveMigrationReconciliationInput>,
|
||||
setFeatureFlag: z.strictObject({ orgId: id, flagId: id, enabled: z.boolean() }) satisfies z.ZodType<C.SetFeatureFlagInput>,
|
||||
runChaosDrill: z.strictObject({ orgId: id, drillId: id }) satisfies z.ZodType<C.RunChaosDrillInput>,
|
||||
setPrBotGate: z.strictObject({ orgId: id, gateId: id, enabled: z.boolean() }) satisfies z.ZodType<C.SetPrBotGateInput>,
|
||||
createComplianceExport: z.strictObject({
|
||||
orgId: id, subjectType: z.enum(["employee", "agent"]), subjectId: id, exportType: z.enum(COMPLIANCE_EXPORT_TYPE), requestedByEmployeeId: id,
|
||||
}) satisfies z.ZodType<C.CreateComplianceExportInput>,
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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'));
|
||||
`;
|
||||
@@ -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;
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,6 @@ export class SnapshotService {
|
||||
.map((event) => ({ ...event, success: toBool(event.success) }));
|
||||
const chaosDrills = list<AppSnapshot["chaosDrills"][number]>(this.db, "chaos_drills", "key asc")
|
||||
.map((drill) => ({ ...drill, enabled: toBool(drill.enabled) }));
|
||||
const prBotGates = list<AppSnapshot["prBotGates"][number]>(this.db, "pr_bot_gates", "key asc")
|
||||
.map((gate) => ({ ...gate, enabled: toBool(gate.enabled) }));
|
||||
const rollbackSnapshots = list<AppSnapshot["rollbackSnapshots"][number]>(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<AppSnapshot["auditEvents"][number]>(this.db, "audit_events", "created_at desc", 200),
|
||||
documents: list(this.db, "documents", "updated_at desc"),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -83,9 +83,3 @@ export interface RunChaosDrillInput {
|
||||
orgId: string;
|
||||
drillId: string;
|
||||
}
|
||||
|
||||
export interface SetPrBotGateInput {
|
||||
orgId: string;
|
||||
gateId: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
@@ -83,7 +83,6 @@ import type {
|
||||
SetFilePinnedInput,
|
||||
SetFeatureFlagInput,
|
||||
SetLegalHoldStateInput,
|
||||
SetPrBotGateInput,
|
||||
SetRollbackSnapshotActiveInput,
|
||||
SetRetentionFreezeStateInput,
|
||||
SetScheduledPostStateInput,
|
||||
@@ -190,7 +189,6 @@ export interface DesktopApi {
|
||||
resolveMigrationReconciliation(input: ResolveMigrationReconciliationInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
setFeatureFlag(input: SetFeatureFlagInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
runChaosDrill(input: RunChaosDrillInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
setPrBotGate(input: SetPrBotGateInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
createComplianceExport(input: CreateComplianceExportInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
createAuditReplay(input: CreateAuditReplayInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
createLegalHold(input: CreateLegalHoldInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
type ValueOf<T> = 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<typeof PR_BOT_GATE_KEY>;
|
||||
|
||||
export interface PrBotGateRecord {
|
||||
id: string; orgId: string; key: PrBotGateKey; label: string; gateType: string; enabled: boolean; createdAt: string; updatedAt: string;
|
||||
}
|
||||
@@ -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[];
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user