Q135: remove the thread replay that only digested events and reconstructed nothing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 21:47:48 -04:00
1 parent 403c57785c
commit f8f852c326
21 files changed
+17 -288

No files matched your search

+1 -1
View File
@@ -31,7 +31,7 @@ Model provider keys and sign-ins live in **Settings > Providers**. All keys are
pnpm verify:ci
```
Typecheck, lint, unit, contract, integration, replay-eval, and Electron e2e.
Typecheck, lint, unit, contract, integration, and Electron e2e.
## Build installers
+2 -3
View File
@@ -17,11 +17,10 @@
"test:unit": "vitest run tests/unit",
"test:contract": "vitest run tests/contract",
"test:integration": "vitest run tests/integration",
"test:replay-eval": "vitest run tests/eval",
"test": "pnpm test:unit && pnpm test:contract && pnpm test:integration && pnpm test:replay-eval",
"test": "pnpm test:unit && pnpm test:contract && pnpm test:integration",
"test:e2e": "pnpm build && playwright test -c tests/e2e/playwright.config.ts",
"verify": "pnpm verify:ci",
"verify:ci": "pnpm typecheck && pnpm lint && pnpm test:unit && pnpm test:contract && pnpm test:integration && pnpm test:replay-eval && pnpm test:e2e",
"verify:ci": "pnpm typecheck && pnpm lint && pnpm test:unit && pnpm test:contract && pnpm test:integration && pnpm test:e2e",
"package:dir": "pnpm build && electron-builder --dir --publish never",
"package:win": "pnpm build && electron-builder --win --x64 --publish never",
"package:mac:arm": "pnpm build && electron-builder --mac --arm64 --publish never",
-1
View File
@@ -59,7 +59,6 @@ export const platformContracts = {
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>,
createAuditReplay: z.strictObject({ orgId: id, threadId: id, createdByEmployeeId: id }) satisfies z.ZodType<C.CreateAuditReplayInput>,
createLegalHold: z.strictObject({
orgId: id, entityType: z.enum(["organization", "employee", "agent", "chat_thread", "file_object"]), entityId: id, reason: z.string(), createdByEmployeeId: id,
}) satisfies z.ZodType<C.CreateLegalHoldInput>,
-3
View File
@@ -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 { AuditService } from "../services/audit-service";
import { RollbackService } from "../services/rollback-service";
import { TaskExecutionService } from "../services/task-execution-service";
import { DocumentService } from "../services/document-service";
@@ -77,7 +76,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 audit = new AuditService(deps.connection.db);
const rollback = new RollbackService(deps.connection.db);
const documents = new DocumentService(deps.connection.db);
const knowledge = new KnowledgeService(deps.connection.db);
@@ -377,7 +375,6 @@ export function registerIpcHandlers(deps: IpcDeps): void {
handle("resolveMigrationReconciliation", (input) => mutate(() => rollback.resolveReconciliationItem(input)));
handle("setFeatureFlag", (input) => mutate(() => operations.setFeatureFlag(input)));
handle("createComplianceExport", (input) => mutate(() => compliance.createExport(input)));
handle("createAuditReplay", (input) => mutate(() => audit.createThreadReplay(input)));
handle("createLegalHold", (input) => mutate(() => compliance.createLegalHold(input)));
handle("setLegalHoldState", (input) => mutate(() => compliance.setLegalHoldState(input)));
handle("createRetentionFreeze", (input) => mutate(() => compliance.createRetentionFreeze(input)));
@@ -0,0 +1,7 @@
// Thread "replays" only digested audit events and could not reconstruct a run's context, tool calls or outside state.
export const dropAuditReplaysSchema = `
drop table if exists audit_replay_runs;
insert into schema_migrations(version, name, applied_at)
values (98, 'drop_audit_replays', datetime('now'));
`;
+2
View File
@@ -96,6 +96,7 @@ import { dropConfigCountEvalsSchema } from "./migrations/094-drop-config-count-e
import { dropPrBotGatesSchema } from "./migrations/095-drop-pr-bot-gates";
import { dropChaosDrillsSchema } from "./migrations/096-drop-chaos-drills";
import { dropDrillOnlySloSchema } from "./migrations/097-drop-drill-only-slo";
import { dropAuditReplaysSchema } from "./migrations/098-drop-audit-replays";
interface Migration {
version: number;
@@ -201,6 +202,7 @@ const MIGRATIONS: Migration[] = [
{ version: 95, name: "drop_pr_bot_gates", sql: dropPrBotGatesSchema },
{ version: 96, name: "drop_chaos_drills", sql: dropChaosDrillsSchema },
{ version: 97, name: "drop_drill_only_slo", sql: dropDrillOnlySloSchema },
{ version: 98, name: "drop_audit_replays", sql: dropAuditReplaysSchema },
];
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
-124
View File
@@ -1,36 +1,6 @@
import { createHash } from "node:crypto";
import type { DatabaseSync } from "node:sqlite";
import type { CreateAuditReplayInput } from "../../shared/commands";
import { createId } from "../../shared/ids";
interface AuditEventRow {
id: string;
org_id: string;
entity_type: string;
entity_id: string;
actor_type: string;
actor_id: string | null;
action: string;
payload_json: string;
created_at: string;
}
interface ChatThreadRow {
id: string;
root_message_id: string | null;
}
interface ReplayEvent {
id: string;
entityType: string;
entityId: string;
actorType: string;
actorId: string | null;
action: string;
payload: Record<string, unknown>;
createdAt: string;
}
export class AuditService {
constructor(private readonly db: DatabaseSync) {}
@@ -66,98 +36,4 @@ export class AuditService {
const actor = this.db.prepare(`select id from ${table} where id = ? and org_id = ?`).get(actorId, orgId);
if (!actor) throw new Error(`No ${actorType} "${actorId}" in this organization.`);
}
createThreadReplay(input: CreateAuditReplayInput): string {
const thread = this.requireThread(input.orgId, input.threadId);
this.requireEmployee(input.orgId, input.createdByEmployeeId);
const messageIds = this.threadMessageIds(input.orgId, input.threadId, thread.root_message_id);
const events = this.threadEvents(input.orgId, input.threadId, messageIds);
if (events.length === 0) throw new Error("Thread replay requires audit events.");
const replay = {
threadId: input.threadId,
rootMessageId: thread.root_message_id,
messageIds: [...messageIds].sort(),
events,
};
const replayJson = JSON.stringify(replay);
const eventDigest = createHash("sha256").update(replayJson).digest("hex");
const id = createId("arp");
const now = new Date().toISOString();
this.db.prepare(`
insert into audit_replay_runs(
id, org_id, thread_id, event_count, event_digest, replay_json,
created_by_employee_id, created_at
) values (?, ?, ?, ?, ?, ?, ?, ?)
`).run(id, input.orgId, input.threadId, events.length, eventDigest, replayJson, input.createdByEmployeeId, now);
this.record({
orgId: input.orgId,
entityType: "audit_replay_run",
entityId: id,
actorType: "employee",
actorId: input.createdByEmployeeId,
action: "created",
payload: { threadId: input.threadId, eventCount: events.length, eventDigest },
});
return id;
}
private requireThread(orgId: string, threadId: string): ChatThreadRow {
const thread = this.db.prepare("select id, root_message_id from chat_threads where id = ? and org_id = ?")
.get(threadId, orgId) as ChatThreadRow | undefined;
if (!thread) throw new Error("Chat thread was not found.");
return thread;
}
private requireEmployee(orgId: string, employeeId: string): void {
const employee = this.db.prepare("select id from employees where id = ? and org_id = ?").get(employeeId, orgId);
if (!employee) throw new Error("Audit replay employee was not found.");
}
private threadMessageIds(orgId: string, threadId: string, rootMessageId: string | null): Set<string> {
const ids = new Set<string>();
if (rootMessageId) ids.add(rootMessageId);
const rows = this.db.prepare("select id from chat_messages where org_id = ? and thread_id = ? order by created_at asc")
.all(orgId, threadId) as Array<{ id: string }>;
for (const row of rows) ids.add(row.id);
return ids;
}
private threadEvents(orgId: string, threadId: string, messageIds: Set<string>): ReplayEvent[] {
const rows = this.db.prepare("select * from audit_events where org_id = ? order by created_at asc, id asc")
.all(orgId) as unknown as AuditEventRow[];
return rows
.filter((row) => eventMatchesThread(row, threadId, messageIds))
.map((row) => ({
id: row.id,
entityType: row.entity_type,
entityId: row.entity_id,
actorType: row.actor_type,
actorId: row.actor_id,
action: row.action,
payload: parsePayload(row.payload_json),
createdAt: row.created_at,
}));
}
}
function eventMatchesThread(row: AuditEventRow, threadId: string, messageIds: Set<string>): boolean {
if (row.entity_type === "audit_replay_run") return false;
if (row.entity_type === "chat_thread" && row.entity_id === threadId) return true;
if (row.entity_type === "chat_message" && messageIds.has(row.entity_id)) return true;
const payload = parsePayload(row.payload_json);
return stringField(payload, "threadId") === threadId
|| stringField(payload, "sourceThreadId") === threadId
|| messageIds.has(stringField(payload, "messageId"))
|| messageIds.has(stringField(payload, "sourceMessageId"));
}
function parsePayload(payloadJson: string): Record<string, unknown> {
const parsed = JSON.parse(payloadJson) as unknown;
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("Audit event payload is invalid.");
return parsed as Record<string, unknown>;
}
function stringField(payload: Record<string, unknown>, key: string): string {
const value = payload[key];
return typeof value === "string" ? value : "";
}
-2
View File
@@ -200,8 +200,6 @@ export class ComplianceService {
.run(orgId, subjectId);
this.db.prepare("update retention_freezes set created_by_employee_id = null where org_id = ? and created_by_employee_id = ?")
.run(orgId, subjectId);
this.db.prepare("update audit_replay_runs set created_by_employee_id = null where org_id = ? and created_by_employee_id = ?")
.run(orgId, subjectId);
this.db.prepare("update rollback_snapshots set created_by_employee_id = null where org_id = ? and created_by_employee_id = ?")
.run(orgId, subjectId);
this.db.prepare("update migration_reconciliation_items set resolved_by_employee_id = null where org_id = ? and resolved_by_employee_id = ?")
-4
View File
@@ -70,7 +70,6 @@ const LEGACY_TABLES = [
"legal_holds",
"retention_freezes",
"search_documents",
"audit_replay_runs",
"rollback_snapshots",
"migration_cutover_events",
"migration_reconciliation_items",
@@ -239,9 +238,6 @@ const RELATION_CHECKS: RelationCheck[] = [
{ table: "legal_holds", column: "created_by_employee_id", referencedTable: "employees", referencedColumn: "id", reason: "dangling_creator_employee_id" },
{ table: "retention_freezes", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" },
{ table: "retention_freezes", column: "created_by_employee_id", referencedTable: "employees", referencedColumn: "id", reason: "dangling_creator_employee_id" },
{ table: "audit_replay_runs", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" },
{ table: "audit_replay_runs", column: "thread_id", referencedTable: "chat_threads", referencedColumn: "id", reason: "dangling_thread_id" },
{ table: "audit_replay_runs", column: "created_by_employee_id", referencedTable: "employees", referencedColumn: "id", reason: "dangling_creator_employee_id" },
{ table: "rollback_snapshots", column: "org_id", referencedTable: "organizations", referencedColumn: "id", reason: "dangling_org_id" },
{ table: "rollback_snapshots", column: "migration_report_id", referencedTable: "migration_reports", referencedColumn: "id", reason: "dangling_migration_report_id" },
{ table: "rollback_snapshots", column: "created_by_employee_id", referencedTable: "employees", referencedColumn: "id", reason: "dangling_creator_employee_id" },
-1
View File
@@ -156,7 +156,6 @@ export class SnapshotService {
featureFlags,
sloTargets: list(this.db, "slo_targets", "signal_key asc"),
operationalEvents,
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"),
knowledgeEntries: list(this.db, "knowledge_entries", "updated_at desc"),
-1
View File
@@ -81,7 +81,6 @@ const api: DesktopApi = {
resolveMigrationReconciliation: (input) => invoke(IPC_CHANNELS.resolveMigrationReconciliation, input),
setFeatureFlag: (input) => invoke(IPC_CHANNELS.setFeatureFlag, input),
createComplianceExport: (input) => invoke(IPC_CHANNELS.createComplianceExport, input),
createAuditReplay: (input) => invoke(IPC_CHANNELS.createAuditReplay, input),
createLegalHold: (input) => invoke(IPC_CHANNELS.createLegalHold, input),
setLegalHoldState: (input) => invoke(IPC_CHANNELS.setLegalHoldState, input),
createRetentionFreeze: (input) => invoke(IPC_CHANNELS.createRetentionFreeze, input),
-10
View File
@@ -1,10 +0,0 @@
export interface AuditReplayRunRecord {
id: string;
orgId: string;
threadId: string;
eventCount: number;
eventDigest: string;
replayJson: string;
createdByEmployeeId: string | null;
createdAt: string;
}
-6
View File
@@ -37,12 +37,6 @@ export interface CreateComplianceExportInput {
requestedByEmployeeId: string;
}
export interface CreateAuditReplayInput {
orgId: string;
threadId: string;
createdByEmployeeId: string;
}
export interface CreateLegalHoldInput {
orgId: string;
entityType: LegalHoldEntityType;
-2
View File
@@ -38,7 +38,6 @@ import type {
UpdateAutomationRuleInput,
UpdateDocumentInput,
UpdateTaskInput,
CreateAuditReplayInput,
CreateChatSpaceInput,
CreateComplianceExportInput,
CreateLegalHoldInput,
@@ -188,7 +187,6 @@ export interface DesktopApi {
resolveMigrationReconciliation(input: ResolveMigrationReconciliationInput): Promise<DesktopResult<AppSnapshot>>;
setFeatureFlag(input: SetFeatureFlagInput): Promise<DesktopResult<AppSnapshot>>;
createComplianceExport(input: CreateComplianceExportInput): Promise<DesktopResult<AppSnapshot>>;
createAuditReplay(input: CreateAuditReplayInput): Promise<DesktopResult<AppSnapshot>>;
createLegalHold(input: CreateLegalHoldInput): Promise<DesktopResult<AppSnapshot>>;
setLegalHoldState(input: SetLegalHoldStateInput): Promise<DesktopResult<AppSnapshot>>;
createRetentionFreeze(input: CreateRetentionFreezeInput): Promise<DesktopResult<AppSnapshot>>;
-1
View File
@@ -1,7 +1,6 @@
import type { ProviderDataPolicy } from "./provider-policy-domain";
export { BOARD_INTERVENTION_ACTION, BOARD_INTERVENTION_MODE, CHAT_THREAD_BOARD_STATE, type BoardInterventionAction, type BoardInterventionMode, type BoardInterventionRecord, type ChatThreadBoardState } from "./board-domain";
export type { ChatSpaceRecord, ChatMessageRecord, ChatMembershipRecord, ChatThreadRecord, ChatAttachmentRecord, ChatMentionRecord, ChatSlashInvocationRecord, ChatPresenceRecord, ChatReadStateRecord } from "./chat-domain";
export type { AuditReplayRunRecord } from "./audit-domain";
export { COMPLIANCE_EXPORT_STATE, COMPLIANCE_EXPORT_TYPE, type ComplianceExportRecord, type ComplianceExportState, type ComplianceExportType, type ComplianceSubjectType, type LegalHoldEntityType, type LegalHoldRecord, type RetentionFreezeRecord, type RetentionFreezeScopeType } from "./compliance-domain";
export { ACCESS_DECISION, IDENTITY_SOURCE, ROLE_KEY, type AccessDecision, type AccessDecisionRecord, type EmployeeIdentityRecord, type IdentitySource, type PrincipalType, type RoleGrantRecord, type RoleKey, type ServiceIdentityRecord } from "./identity-domain";
export { ROUTE_TO_TYPE, type ApprovalAssignmentRecord, type ApprovalRoutingRuleRecord, type HiringPolicyRecord, type OrgDepartmentRecord, type RouteToType } from "./org-governance-domain";
-1
View File
@@ -54,7 +54,6 @@ export const IPC_CHANNELS = {
resolveMigrationReconciliation: "agentcompany:resolve-migration-reconciliation",
setFeatureFlag: "agentcompany:set-feature-flag",
createComplianceExport: "agentcompany:create-compliance-export",
createAuditReplay: "agentcompany:create-audit-replay",
createLegalHold: "agentcompany:create-legal-hold",
setLegalHoldState: "agentcompany:set-legal-hold-state",
createRetentionFreeze: "agentcompany:create-retention-freeze",
-2
View File
@@ -1,5 +1,4 @@
import type { UsageStatsRecord } from "./usage-stats";
import type { AuditReplayRunRecord } from "./audit-domain";
import type { IntegrationKeyStatus } from "./commands/integration-keys";
import type { BoardInterventionRecord as BoardInterventionRecordModel } from "./board-domain";
import type {
@@ -136,7 +135,6 @@ export interface AppSnapshot {
featureFlags: FeatureFlagRecord[];
sloTargets: SloTargetRecord[];
operationalEvents: OperationalEventRecord[];
auditReplayRuns: AuditReplayRunRecord[];
auditEvents: AuditEventRecord[];
documents: DocumentRecord[];
knowledgeEntries: KnowledgeEntryRecord[];
-70
View File
@@ -1,70 +0,0 @@
import { createHash, 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 { AuditService } from "../../src/main/services/audit-service";
import { ChatService } from "../../src/main/services/chat-service";
import { OrganizationService } from "../../src/main/services/organization-service";
import { SnapshotService } from "../../src/main/services/snapshot-service";
function createConnection(): DatabaseConnection {
return new DatabaseConnection(join(tmpdir(), `agentcompany-new-replay-${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("replay eval", () => {
test("creates deterministic thread replay evidence and records the replay digest", () => {
const connection = createConnection();
const organization = new OrganizationService(connection.db);
const chat = new ChatService(connection.db);
const audit = new AuditService(connection.db);
const snapshot = new SnapshotService(connection.db);
let orgId = "";
let employeeId = "";
let threadId = "";
let replayRunId = "";
connection.transaction(() => {
orgId = createOrganization(organization);
employeeId = (connection.db.prepare("select id from employees where org_id = ?").get(orgId) as { id: string }).id;
const spaceId = chat.createSpace({
orgId,
spaceType: "project",
name: "release",
topic: "Release coordination",
visibility: "workspace",
});
const messageId = chat.sendMessage({
orgId,
spaceId,
threadId: null,
senderType: "employee",
senderId: employeeId,
bodyMd: "/escalate replay the release approval trail",
attachmentFileIds: [],
});
threadId = (connection.db.prepare("select thread_id from chat_messages where id = ?").get(messageId) as { thread_id: string }).thread_id;
replayRunId = audit.createThreadReplay({ orgId, threadId, createdByEmployeeId: employeeId });
});
const state = snapshot.load();
const replayRun = state.auditReplayRuns.find((run) => run.id === replayRunId);
if (!replayRun) throw new Error("Audit replay run was not created.");
const replay = JSON.parse(replayRun.replayJson) as { events: Array<{ entityType: string; action: string }> };
expect(replayRun.threadId).toBe(threadId);
expect(replayRun.eventDigest).toBe(createHash("sha256").update(replayRun.replayJson).digest("hex"));
expect(replayRun.eventCount).toBe(replay.events.length);
expect(replay.events).toContainEqual(expect.objectContaining({ entityType: "chat_message", action: "sent" }));
expect(replay.events).toContainEqual(expect.objectContaining({ entityType: "approval", action: "requested" }));
connection.close();
});
});
+4 -51
View File
@@ -3,10 +3,8 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
import { DatabaseConnection } from "../../src/main/database/connection";
import { AuditService } from "../../src/main/services/audit-service";
import { ChatService } from "../../src/main/services/chat-service";
import { OrganizationService } from "../../src/main/services/organization-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-audit-${randomUUID()}.db`));
@@ -35,55 +33,10 @@ describe("audit service", () => {
connection.close();
});
test("creates thread scoped replay runs from immutable audit events", () => {
test("offers no thread replay that only digests events and reconstructs nothing", () => {
const connection = createConnection();
const organization = new OrganizationService(connection.db);
const chat = new ChatService(connection.db);
const audit = new AuditService(connection.db);
const snapshot = new SnapshotService(connection.db);
let orgId = "";
let employeeId = "";
let threadId = "";
connection.transaction(() => {
orgId = organization.createOrganization({
organizationName: "Acme Operations",
employeeName: "Dana Lee",
mission: "Coordinate agents with auditable approvals.",
goal: "Ship a verified control plane.",
});
employeeId = (connection.db.prepare("select id from employees where org_id = ?").get(orgId) as { id: string }).id;
const spaceId = chat.createSpace({
orgId,
spaceType: "project",
name: "release",
topic: "Release coordination",
visibility: "workspace",
});
const messageId = chat.sendMessage({
orgId,
spaceId,
threadId: null,
senderType: "employee",
senderId: employeeId,
bodyMd: "/escalate review release plan",
attachmentFileIds: [],
});
threadId = (connection.db.prepare("select thread_id from chat_messages where id = ?").get(messageId) as { thread_id: string }).thread_id;
audit.createThreadReplay({ orgId, threadId, createdByEmployeeId: employeeId });
});
const state = snapshot.load();
const run = state.auditReplayRuns[0];
if (!run) throw new Error("Audit replay run was not created.");
const replay = JSON.parse(run.replayJson) as { events: Array<{ entityType: string; action: string }> };
expect(run.threadId).toBe(threadId);
expect(run.eventDigest).toHaveLength(64);
expect(run.eventCount).toBe(replay.events.length);
expect(replay.events).toContainEqual(expect.objectContaining({ entityType: "chat_message", action: "sent" }));
expect(replay.events).toContainEqual(expect.objectContaining({ entityType: "approval", action: "requested" }));
expect(replay.events.some((event) => event.entityType === "audit_replay_run")).toBe(false);
expect(state.auditEvents.some((event) => event.entityType === "audit_replay_run" && event.action === "created")).toBe(true);
expect(connection.db.prepare("select name from sqlite_master where type = 'table' and name = 'audit_replay_runs'").all()).toEqual([]);
expect(Object.keys(IPC_CHANNELS)).not.toContain("createAuditReplay");
connection.close();
});
});
+1 -1
View File
@@ -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, 95, 96, 97]);
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]);
connection.close();
});
-4
View File
@@ -171,7 +171,6 @@ describe("migration service", () => {
create table legal_holds(id text primary key, org_id text, created_by_employee_id text);
create table retention_freezes(id text primary key, org_id text, created_by_employee_id text);
create table search_documents(entity_id text);
create table audit_replay_runs(id text primary key, org_id text, thread_id text, created_by_employee_id text);
create table rollback_snapshots(id text primary key, org_id text, migration_report_id text, created_by_employee_id text);
create table migration_cutover_events(id text primary key, org_id text, migration_report_id text, rollback_snapshot_id text);
create table migration_reconciliation_items(id text primary key, org_id text, migration_report_id text, rollback_snapshot_id text, resolved_by_employee_id text);
@@ -237,7 +236,6 @@ describe("migration service", () => {
insert into legal_holds(id, org_id, created_by_employee_id) values ('hold-1', 'org-1', 'emp-1');
insert into retention_freezes(id, org_id, created_by_employee_id) values ('freeze-1', 'org-1', 'emp-1');
insert into search_documents(entity_id) values ('task-1');
insert into audit_replay_runs(id, org_id, thread_id, created_by_employee_id) values ('replay-1', 'org-1', 'thread-1', 'emp-1');
insert into rollback_snapshots(id, org_id, migration_report_id, created_by_employee_id) values ('rollback-1', 'org-1', 'migration-1', 'emp-1');
insert into migration_cutover_events(id, org_id, migration_report_id, rollback_snapshot_id) values ('cutover-1', 'org-1', 'migration-1', 'rollback-1');
insert into migration_reconciliation_items(id, org_id, migration_report_id, rollback_snapshot_id, resolved_by_employee_id) values ('reconcile-1', 'org-1', 'migration-1', 'rollback-1', 'emp-1');
@@ -281,7 +279,6 @@ describe("migration service", () => {
expect(body.tableCounts.social_approval_routes).toBe(1);
expect(body.tableCounts.social_audit_records).toBe(1);
expect(body.tableCounts.compliance_exports).toBe(1);
expect(body.tableCounts.audit_replay_runs).toBe(1);
expect(body.tableCounts.rollback_snapshots).toBe(1);
expect(body.tableCounts.migration_cutover_events).toBe(1);
expect(body.tableCounts.migration_reconciliation_items).toBe(1);
@@ -295,7 +292,6 @@ describe("migration service", () => {
expect(body.idMaps.board_interventions).toEqual(["bdi-1"]);
expect(body.idMaps.file_handoff_records).toEqual(["handoff-1"]);
expect(body.idMaps.retention_freezes).toEqual(["freeze-1"]);
expect(body.idMaps.audit_replay_runs).toEqual(["replay-1"]);
expect(body.idMaps.rollback_snapshots).toEqual(["rollback-1"]);
expect(body.idMaps.migration_cutover_events).toEqual(["cutover-1"]);
expect(body.idMaps.migration_reconciliation_items).toEqual(["reconcile-1"]);