diff --git a/knowledge-fs/apps/api/src/migrate.test.ts b/knowledge-fs/apps/api/src/migrate.test.ts index 7ee57aa36c5..6ddc9610f0a 100644 --- a/knowledge-fs/apps/api/src/migrate.test.ts +++ b/knowledge-fs/apps/api/src/migrate.test.ts @@ -151,6 +151,8 @@ describe("runApiDatabaseMigrations", () => { "insert", "schema", "insert", + "schema", + "insert", ]); expect(migrationSql).toHaveLength(expectedPostgresMigrationIds.length); expect(migrationSql[2]).toContain("-- Migration id: 0003_projection_set_publications\n"); @@ -233,6 +235,7 @@ describe("runApiDatabaseMigrations", () => { "-- Migration id: 0050_deletion_job_capability_provenance\n", ); expect(migrationSql[50]).toContain("-- Migration id: 0051_failed_query_trace_outcome_index\n"); + expect(migrationSql[51]).toContain("-- Migration id: 0052_namespace_source_previews\n"); expect(closed).toBe(true); }); diff --git a/knowledge-fs/apps/api/src/repository-options.test.ts b/knowledge-fs/apps/api/src/repository-options.test.ts index 2452c1cb19c..9956bf4348d 100644 --- a/knowledge-fs/apps/api/src/repository-options.test.ts +++ b/knowledge-fs/apps/api/src/repository-options.test.ts @@ -111,6 +111,7 @@ describe("createApiDatabaseRepositories", () => { knowledgeSpaceProvisioning: expect.any(Object), knowledgeSpaceUnpublishedProfileActivations: expect.any(Object), legacySpacePublicationBootstraps: expect.any(Object), + namespaceSourcePreviews: expect.any(Object), pageIndexFindability: expect.any(Object), pageIndexUpgradeBackfills: expect.any(Object), qualityControl: expect.any(Object), diff --git a/knowledge-fs/packages/adapters/src/migration-runner.test.ts b/knowledge-fs/packages/adapters/src/migration-runner.test.ts index a3d4ed4e4cc..f389123db29 100644 --- a/knowledge-fs/packages/adapters/src/migration-runner.test.ts +++ b/knowledge-fs/packages/adapters/src/migration-runner.test.ts @@ -59,6 +59,7 @@ const deletionActiveScopeIndexesMigrationId = "0048_deletion_active_scope_indexe const answerTraceQueryImagesMigrationId = "0049_answer_trace_query_images"; const deletionJobCapabilityProvenanceMigrationId = "0050_deletion_job_capability_provenance"; const failedQueryTraceOutcomeIndexMigrationId = "0051_failed_query_trace_outcome_index"; +const namespaceSourcePreviewsMigrationId = "0052_namespace_source_previews"; const migrationsAfterDurableDeletion = [ versionedSpaceProfilesMigrationId, profilePublicationBindingsMigrationId, @@ -94,6 +95,7 @@ const migrationsAfterDurableDeletion = [ answerTraceQueryImagesMigrationId, deletionJobCapabilityProvenanceMigrationId, failedQueryTraceOutcomeIndexMigrationId, + namespaceSourcePreviewsMigrationId, ] as const; const migrationsAfterTidbBaselineRepair = [ spaceAccessControlMigrationId, diff --git a/knowledge-fs/packages/database/src/migration-artifacts.generated.ts b/knowledge-fs/packages/database/src/migration-artifacts.generated.ts index ba908287a72..1a22941538f 100644 --- a/knowledge-fs/packages/database/src/migration-artifacts.generated.ts +++ b/knowledge-fs/packages/database/src/migration-artifacts.generated.ts @@ -105,4 +105,6 @@ export const migrationArtifacts = [ { content: "-- Knowledge Platform schema migration\n-- Migration id: 0050_deletion_job_capability_provenance\n-- Dialect: tidb\n-- A space deletion job outlives the space-owned grant that authorized it. Preserve the grant id\n-- as immutable audit provenance without a live FK that blocks the terminal space delete.\n\nALTER TABLE `deletion_jobs`\n DROP FOREIGN KEY `deletion_jobs_capability_grant_fk`;\n\nALTER TABLE `deletion_retry_audits`\n DROP FOREIGN KEY `deletion_retry_audits_capability_grant_fk`;\n", path: "packages/database/migrations/0050_deletion_job_capability_provenance.tidb.sql" }, { content: "-- Knowledge Platform schema migration\n-- Migration id: 0051_failed_query_trace_outcome_index\n-- Dialect: postgres\n-- Supports Overview outcome joins by exact trace and trigger without scanning space history.\n\nCREATE INDEX IF NOT EXISTS \"failed_queries_trace_trigger_idx\"\n ON \"failed_queries\" (\n \"tenant_id\", \"knowledge_space_id\", \"answer_trace_id\", \"trigger\"\n )\n WHERE \"answer_trace_id\" IS NOT NULL;\n", path: "packages/database/migrations/0051_failed_query_trace_outcome_index.postgres.sql" }, { content: "-- Knowledge Platform schema migration\n-- Migration id: 0051_failed_query_trace_outcome_index\n-- Dialect: tidb\n-- Supports Overview outcome joins by exact trace and trigger without scanning space history.\n\nCREATE INDEX IF NOT EXISTS `failed_queries_trace_trigger_idx`\n ON `failed_queries` (\n `tenant_id`, `knowledge_space_id`, `answer_trace_id`\n );\n", path: "packages/database/migrations/0051_failed_query_trace_outcome_index.tidb.sql" }, + { content: "-- Knowledge Platform schema migration\n-- Migration id: 0052_namespace_source_previews\n-- Dialect: postgres\n\nCREATE TABLE IF NOT EXISTS \"namespace_source_preview_jobs\" (\n \"id\" uuid PRIMARY KEY,\n \"tenant_id\" varchar(255) NOT NULL,\n \"account_id\" varchar(255) NOT NULL,\n \"status\" varchar(32) NOT NULL,\n \"provider_config\" jsonb NOT NULL,\n \"configuration_fingerprint\" varchar(128) NOT NULL,\n \"expires_at\" timestamptz NOT NULL,\n \"consumed_at\" timestamptz NULL,\n \"content_cleaned_at\" timestamptz NULL,\n \"import_workflow_id\" uuid NULL,\n \"error_code\" varchar(128) NULL,\n \"created_at\" timestamptz NOT NULL,\n \"updated_at\" timestamptz NOT NULL\n);\nCREATE INDEX IF NOT EXISTS \"namespace_source_preview_jobs_claim_idx\"\n ON \"namespace_source_preview_jobs\" (\"status\", \"expires_at\", \"created_at\");\nCREATE INDEX IF NOT EXISTS \"namespace_source_preview_jobs_owner_idx\"\n ON \"namespace_source_preview_jobs\" (\"tenant_id\", \"account_id\", \"created_at\");\nCREATE INDEX IF NOT EXISTS \"namespace_source_preview_jobs_cleanup_idx\"\n ON \"namespace_source_preview_jobs\" (\"status\", \"content_cleaned_at\", \"updated_at\");\n\nCREATE TABLE IF NOT EXISTS \"namespace_source_preview_pages\" (\n \"job_id\" uuid NOT NULL REFERENCES \"namespace_source_preview_jobs\" (\"id\") ON DELETE CASCADE,\n \"page_id\" varchar(128) NOT NULL,\n \"source_url\" varchar(4096) NOT NULL,\n \"title\" varchar(500) NULL,\n \"description\" text NULL,\n \"content_hash\" varchar(64) NOT NULL,\n \"content_object_key\" varchar(2048) NOT NULL,\n \"created_at\" timestamptz NOT NULL,\n PRIMARY KEY (\"job_id\", \"page_id\")\n);\n", path: "packages/database/migrations/0052_namespace_source_previews.postgres.sql" }, + { content: "-- Knowledge Platform schema migration\n-- Migration id: 0052_namespace_source_previews\n-- Dialect: tidb\n\nCREATE TABLE IF NOT EXISTS `namespace_source_preview_jobs` (\n `id` char(36) PRIMARY KEY,\n `tenant_id` varchar(255) NOT NULL,\n `account_id` varchar(255) NOT NULL,\n `status` varchar(32) NOT NULL,\n `provider_config` json NOT NULL,\n `configuration_fingerprint` varchar(128) NOT NULL,\n `expires_at` datetime(6) NOT NULL,\n `consumed_at` datetime(6) NULL,\n `content_cleaned_at` datetime(6) NULL,\n `import_workflow_id` char(36) NULL,\n `error_code` varchar(128) NULL,\n `created_at` datetime(6) NOT NULL,\n `updated_at` datetime(6) NOT NULL,\n INDEX `namespace_source_preview_jobs_claim_idx` (`status`, `expires_at`, `created_at`),\n INDEX `namespace_source_preview_jobs_owner_idx` (`tenant_id`, `account_id`, `created_at`),\n INDEX `namespace_source_preview_jobs_cleanup_idx` (`status`, `content_cleaned_at`, `updated_at`)\n);\n\nCREATE TABLE IF NOT EXISTS `namespace_source_preview_pages` (\n `job_id` char(36) NOT NULL,\n `page_id` varchar(128) NOT NULL,\n `source_url` varchar(4096) NOT NULL,\n `title` varchar(500) NULL,\n `description` text NULL,\n `content_hash` varchar(64) NOT NULL,\n `content_object_key` varchar(2048) NOT NULL,\n `created_at` datetime(6) NOT NULL,\n PRIMARY KEY (`job_id`, `page_id`),\n CONSTRAINT `namespace_source_preview_pages_job_fk` FOREIGN KEY (`job_id`) REFERENCES `namespace_source_preview_jobs` (`id`) ON DELETE CASCADE\n);\n", path: "packages/database/migrations/0052_namespace_source_previews.tidb.sql" }, ] as const satisfies readonly MigrationArtifact[]; diff --git a/knowledge-fs/packages/database/src/migration-file.test.ts b/knowledge-fs/packages/database/src/migration-file.test.ts index 260db57668f..787d153e2b2 100644 --- a/knowledge-fs/packages/database/src/migration-file.test.ts +++ b/knowledge-fs/packages/database/src/migration-file.test.ts @@ -162,6 +162,8 @@ describe("migration file rendering", () => { "packages/database/migrations/0050_deletion_job_capability_provenance.tidb.sql", "packages/database/migrations/0051_failed_query_trace_outcome_index.postgres.sql", "packages/database/migrations/0051_failed_query_trace_outcome_index.tidb.sql", + "packages/database/migrations/0052_namespace_source_previews.postgres.sql", + "packages/database/migrations/0052_namespace_source_previews.tidb.sql", ]); const workflowCapturePostgres = artifacts.find( (artifact) => @@ -943,6 +945,7 @@ describe("migration file rendering", () => { "packages/database/migrations/0049_answer_trace_query_images.postgres.sql", "packages/database/migrations/0050_deletion_job_capability_provenance.postgres.sql", "packages/database/migrations/0051_failed_query_trace_outcome_index.postgres.sql", + "packages/database/migrations/0052_namespace_source_previews.postgres.sql", ]); expect( getPendingMigrationArtifacts({ @@ -998,6 +1001,7 @@ describe("migration file rendering", () => { "0049_answer_trace_query_images", "0050_deletion_job_capability_provenance", "0051_failed_query_trace_outcome_index", + "0052_namespace_source_previews", ], dialect: "postgres", }), diff --git a/knowledge-fs/scripts/export-capability-v2-operations.test.mjs b/knowledge-fs/scripts/export-capability-v2-operations.test.mjs index df64502a46d..217f0f1414e 100644 --- a/knowledge-fs/scripts/export-capability-v2-operations.test.mjs +++ b/knowledge-fs/scripts/export-capability-v2-operations.test.mjs @@ -20,7 +20,7 @@ test("Capability v2 operation export is deterministic and includes internal life ); const document = JSON.parse(readFileSync(output, "utf8")); assert.equal(document.schemaVersion, 1); - assert.equal(new Set(document.operations.map((operation) => operation.operationId)).size, 117); + assert.equal(new Set(document.operations.map((operation) => operation.operationId)).size, 121); assert.deepEqual( document.operations.find( (operation) => operation.operationId === "getDocumentMultimodalManifest",