From db099360850dfbc9ad54a45fc6e94d8015335bab Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Sun, 24 May 2026 01:00:20 +0200 Subject: [PATCH] refactor: remove legacy compatibility shims (#208) --- packages/cli/src/context-build-view.test.ts | 4 - packages/cli/src/context-build-view.ts | 2 +- .../historic-sql/connection-dialect.ts | 20 +---- .../local-ingest-acceptance.test.ts | 8 +- .../src/context/ingest/local-adapters.test.ts | 33 +++----- .../cli/src/context/ingest/local-adapters.ts | 10 +-- .../cli/src/context/project/config.test.ts | 28 +------ packages/cli/src/context/project/config.ts | 18 ----- .../context/project/driver-schemas.test.ts | 4 +- .../cli/src/context/project/driver-schemas.ts | 2 +- packages/cli/src/doctor.test.ts | 11 +-- packages/cli/src/ingest.test.ts | 32 ++++---- packages/cli/src/local-adapters.test.ts | 48 +++--------- packages/cli/src/setup-databases.test.ts | 77 ------------------- packages/cli/src/setup-databases.ts | 68 +++------------- packages/cli/src/setup-project.test.ts | 32 -------- packages/cli/src/status-project.ts | 25 ------ 17 files changed, 61 insertions(+), 361 deletions(-) diff --git a/packages/cli/src/context-build-view.test.ts b/packages/cli/src/context-build-view.test.ts index c1550219..089124f5 100644 --- a/packages/cli/src/context-build-view.test.ts +++ b/packages/cli/src/context-build-view.test.ts @@ -121,10 +121,6 @@ describe('parseIngestSummary', () => { expect(parseIngestSummary('Tasks: 5\nStatus: done')).toBe('5 tasks'); }); - it('still parses the legacy "Work units:" wording for backward compat', () => { - expect(parseIngestSummary('Work units: 7\nStatus: done')).toBe('7 tasks'); - }); - it('extracts saved memory alone when no task count', () => { expect(parseIngestSummary('Saved memory: 3 wiki, 2 SL')).toBe('3 wiki, 2 SL'); }); diff --git a/packages/cli/src/context-build-view.ts b/packages/cli/src/context-build-view.ts index 9f6e5f78..49ddd3eb 100644 --- a/packages/cli/src/context-build-view.ts +++ b/packages/cli/src/context-build-view.ts @@ -462,7 +462,7 @@ export function parseScanSummary(output: string): string | null { export function parseIngestSummary(output: string): string | null { const savedMemory = output.match(/Saved memory: (.+)/); if (savedMemory) return savedMemory[1]; - const tasks = output.match(/(?:Tasks|Work units): (\d+)/); + const tasks = output.match(/Tasks: (\d+)/); if (tasks) return `${tasks[1]} tasks`; return null; } diff --git a/packages/cli/src/context/ingest/adapters/historic-sql/connection-dialect.ts b/packages/cli/src/context/ingest/adapters/historic-sql/connection-dialect.ts index e81966b5..846ce098 100644 --- a/packages/cli/src/context/ingest/adapters/historic-sql/connection-dialect.ts +++ b/packages/cli/src/context/ingest/adapters/historic-sql/connection-dialect.ts @@ -1,20 +1,9 @@ import type { HistoricSqlDialect } from './types.js'; -const KNOWN_DIALECTS = ['postgres', 'bigquery', 'snowflake'] as const; - -function isKnownDialect(value: string): value is HistoricSqlDialect { - return (KNOWN_DIALECTS as readonly string[]).includes(value); -} - function recordOrNull(value: unknown): Record | null { return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record) : null; } -function historicSqlRecord(connection: unknown): Record | null { - const conn = recordOrNull(connection); - return conn ? recordOrNull(conn.historicSql) : null; -} - function queryHistoryRecord(connection: unknown): Record | null { const conn = recordOrNull(connection); const context = conn ? recordOrNull(conn.context) : null; @@ -22,11 +11,7 @@ function queryHistoryRecord(connection: unknown): Record | null } export function isQueryHistoryEnabled(connection: unknown): boolean { - const queryHistory = queryHistoryRecord(connection); - if (queryHistory) { - return queryHistory.enabled === true; - } - return historicSqlRecord(connection)?.enabled === true; + return queryHistoryRecord(connection)?.enabled === true; } /** @@ -43,6 +28,5 @@ export function queryHistoryDialectForConnection(connection: unknown): HistoricS if (driver === 'postgres' || driver === 'postgresql') return 'postgres'; if (driver === 'bigquery') return 'bigquery'; if (driver === 'snowflake') return 'snowflake'; - const legacy = String(historicSqlRecord(connection)?.dialect ?? '').toLowerCase(); - return isKnownDialect(legacy) ? legacy : null; + return null; } diff --git a/packages/cli/src/context/ingest/adapters/historic-sql/local-ingest-acceptance.test.ts b/packages/cli/src/context/ingest/adapters/historic-sql/local-ingest-acceptance.test.ts index 62d5ba42..a443f995 100644 --- a/packages/cli/src/context/ingest/adapters/historic-sql/local-ingest-acceptance.test.ts +++ b/packages/cli/src/context/ingest/adapters/historic-sql/local-ingest-acceptance.test.ts @@ -152,10 +152,10 @@ async function writeHistoricSqlProject(project: KtxLocalProject): Promise { warehouse: { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', - historicSql: { - enabled: true, - dialect: 'postgres', - minExecutions: 7, - maxTemplatesPerRun: 123, - filters: { - serviceAccounts: { patterns: ['^svc_'], mode: 'exclude' }, - dropTrivialProbes: true, + context: { + queryHistory: { + enabled: true, + minExecutions: 7, + maxTemplatesPerRun: 123, + filters: { + serviceAccounts: { patterns: ['^svc_'], mode: 'exclude' }, + dropTrivialProbes: true, + }, }, }, }, @@ -236,22 +237,6 @@ describe('local ingest adapters', () => { }); }); - it('prefers context.queryHistory over legacy historicSql', async () => { - const project = projectWithConnections({ - warehouse: { - driver: 'postgres', - historicSql: { enabled: true, dialect: 'postgres', windowDays: 90 }, - context: { queryHistory: { enabled: true, windowDays: 30 } }, - }, - }); - const adapter = { source: 'historic-sql' } as never; - - await expect(localPullConfigForAdapter(project, adapter, 'warehouse')).resolves.toMatchObject({ - dialect: 'postgres', - minExecutions: 5, - }); - }); - it('rejects local historic-sql pulls when the connection has not enabled historic SQL', async () => { const historicSql = createDefaultLocalIngestAdapters(project, { historicSql: { diff --git a/packages/cli/src/context/ingest/local-adapters.ts b/packages/cli/src/context/ingest/local-adapters.ts index ea7556e7..e7c99146 100644 --- a/packages/cli/src/context/ingest/local-adapters.ts +++ b/packages/cli/src/context/ingest/local-adapters.ts @@ -247,16 +247,10 @@ export async function localPullConfigForAdapter( return historicSqlUnifiedPullConfigSchema.parse(options.historicSqlPullConfigOverride); } const queryHistory = queryHistoryPullConfig(connection); - if (queryHistory) { - return historicSqlUnifiedPullConfigSchema.parse(queryHistory); - } - const historicSql = isRecord(connection?.historicSql) ? connection.historicSql : null; - if (historicSql?.enabled !== true) { + if (!queryHistory) { throw new Error(`Connection "${connectionId}" does not have context.queryHistory.enabled: true`); } - return historicSqlUnifiedPullConfigSchema.parse({ - ...historicSql, - }); + return historicSqlUnifiedPullConfigSchema.parse(queryHistory); } if (adapter.source === 'looker') { await seedLocalMappingStateFromKtxYaml(project, connectionId); diff --git a/packages/cli/src/context/project/config.test.ts b/packages/cli/src/context/project/config.test.ts index 55188aa2..28e00f74 100644 --- a/packages/cli/src/context/project/config.test.ts +++ b/packages/cli/src/context/project/config.test.ts @@ -369,7 +369,7 @@ ingest: llm: backend: anthropic `), - ).toThrow('Unsupported ingest.llm: use top-level llm.provider, llm.models, and ingest.workUnits'); + ).toThrow('Unsupported ingest.llm: unknown field'); expect(() => parseKtxProjectConfig(` @@ -377,7 +377,7 @@ scan: enrichment: backend: gateway `), - ).toThrow('Unsupported scan.enrichment.backend: use scan.enrichment.mode'); + ).toThrow('Unsupported scan.enrichment.backend: unknown field'); expect(() => parseKtxProjectConfig(` @@ -387,7 +387,7 @@ scan: llm: backend: gateway `), - ).toThrow('Unsupported scan.enrichment.llm: use top-level llm.provider and llm.models'); + ).toThrow('Unsupported scan.enrichment.llm: unknown field'); expect(() => parseKtxProjectConfig(` @@ -478,28 +478,6 @@ scan: ); }); - it('attaches migration hints for known deprecated keys', () => { - const result = validateKtxProjectConfig(` -ingest: - llm: - backend: anthropic -scan: - enrichment: - backend: none -`); - - expect(result.ok).toBe(false); - const findIssue = (path: string) => result.issues.find((issue) => issue.path === path); - expect(findIssue('ingest.llm')).toMatchObject({ - message: 'Unsupported ingest.llm: use top-level llm.provider, llm.models, and ingest.workUnits', - fix: 'use top-level llm.provider, llm.models, and ingest.workUnits', - }); - expect(findIssue('scan.enrichment.backend')).toMatchObject({ - message: 'Unsupported scan.enrichment.backend: use scan.enrichment.mode', - fix: 'use scan.enrichment.mode', - }); - }); - it('reports YAML parse errors as a root-level issue', () => { const result = validateKtxProjectConfig(': not valid yaml :\n'); expect(result.ok).toBe(false); diff --git a/packages/cli/src/context/project/config.ts b/packages/cli/src/context/project/config.ts index e83f502e..cd0eaa4a 100644 --- a/packages/cli/src/context/project/config.ts +++ b/packages/cli/src/context/project/config.ts @@ -11,15 +11,6 @@ const KTX_WORK_UNIT_FAILURE_MODES = ['abort', 'continue'] as const; const KTX_STORAGE_STATES = ['sqlite', 'postgres'] as const; const KTX_SEARCH_BACKENDS = ['sqlite-fts5', 'postgres-hybrid'] as const; -const DEPRECATED_KEY_HINTS: Record = { - 'llm.provider.provider': 'use llm.provider.backend', - 'ingest.llm': 'use top-level llm.provider, llm.models, and ingest.workUnits', - 'ingest.embeddings.provider': 'use ingest.embeddings.backend', - 'scan.enrichment.backend': 'use scan.enrichment.mode', - 'scan.enrichment.llm': 'use top-level llm.provider and llm.models', - 'scan.enrichment.embeddings.provider': 'use scan.enrichment.embeddings.backend', -}; - const apiCredentialsSchema = z .strictObject({ api_key: z.string().min(1).optional().describe('API key for the provider. Read from this value or the provider-specific environment variable.'), @@ -189,12 +180,7 @@ const setupSchema = z .array(z.string().min(1)) .default([]) .describe('Connection IDs (keys of the top-level `connections` map) that the setup wizard treats as the project\'s primary databases.'), - completed_steps: z - .unknown() - .optional() - .describe('Deprecated. Accepted for backward compatibility but ignored; KTX no longer tracks setup progress here.'), }) - .transform(({ database_connection_ids }) => ({ database_connection_ids })) .describe('Setup-wizard state captured during `ktx setup`.'); const storageGitSchema = z @@ -309,10 +295,6 @@ function formatIssue(issue: z.core.$ZodIssue, input: unknown): KtxConfigIssue[] const keys = (issue as { keys?: readonly string[] }).keys ?? []; return keys.map((key) => { const fullPath = basePath.length > 0 ? `${basePath}.${key}` : key; - const hint = DEPRECATED_KEY_HINTS[fullPath]; - if (hint !== undefined) { - return { path: fullPath, message: `Unsupported ${fullPath}: ${hint}`, fix: hint }; - } return { path: fullPath, message: `Unsupported ${fullPath}: unknown field` }; }); } diff --git a/packages/cli/src/context/project/driver-schemas.test.ts b/packages/cli/src/context/project/driver-schemas.test.ts index 89862546..252f428f 100644 --- a/packages/cli/src/context/project/driver-schemas.test.ts +++ b/packages/cli/src/context/project/driver-schemas.test.ts @@ -19,12 +19,12 @@ describe('connectionConfigSchema (driver discriminated union)', () => { const parsed = connectionConfigSchema.parse({ driver: 'postgres', url: 'postgres://x', - historicSql: { enabled: true }, + customField: { enabled: true }, context: { queryHistory: { enabled: false } }, }); expect(parsed).toMatchObject({ driver: 'postgres', - historicSql: { enabled: true }, + customField: { enabled: true }, context: { queryHistory: { enabled: false } }, }); }); diff --git a/packages/cli/src/context/project/driver-schemas.ts b/packages/cli/src/context/project/driver-schemas.ts index a3b71bff..3d6d1a84 100644 --- a/packages/cli/src/context/project/driver-schemas.ts +++ b/packages/cli/src/context/project/driver-schemas.ts @@ -35,7 +35,7 @@ function warehouseConnectionSchema(driver: ), }) .describe( - `${driver} warehouse connection. Additional driver-tunable fields (e.g. historicSql, context.queryHistory) are accepted and passed through.`, + `${driver} warehouse connection. Additional driver-tunable fields (e.g. context.queryHistory) are accepted and passed through.`, ); } diff --git a/packages/cli/src/doctor.test.ts b/packages/cli/src/doctor.test.ts index b483ea20..6064cfeb 100644 --- a/packages/cli/src/doctor.test.ts +++ b/packages/cli/src/doctor.test.ts @@ -356,7 +356,7 @@ describe('runKtxDoctor', () => { expect(out).toContain('KTX status'); expect(out).toContain('Config'); expect(out).toContain('Unsupported storrage: unknown field'); - expect(out).toContain('Unsupported ingest.llm: use top-level llm.provider'); + expect(out).toContain('Unsupported ingest.llm: unknown field'); expect(out).toContain('ktx.yaml'); }); @@ -609,11 +609,6 @@ describe('runKtxDoctor', () => { ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', ' readonly: true', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - ' windowDays: 30', - ' concurrency: 4', ' local:', ' driver: sqlite', ' file_path: ./warehouse.db', @@ -651,8 +646,6 @@ describe('runKtxDoctor', () => { const out = testIo.stdout(); expect(out).toContain('Warnings'); expect(out).toContain('connections.warehouse.readonly is no longer used.'); - expect(out).toContain('connections.warehouse.historicSql.concurrency is no longer used.'); - expect(out).toContain('connections.warehouse.historicSql.windowDays does not constrain pg_stat_statements.'); expect(out).toContain('connections.local.file_path was removed.'); expect(out).toContain('connections.docs.last_successful_cursor is local sync state.'); delete process.env.ANTHROPIC_API_KEY; @@ -786,7 +779,7 @@ describe('runKtxDoctor', () => { const out = testIo.stdout(); expect(out).toContain('Unsupported storrage: unknown field'); - expect(out).toContain('Unsupported ingest.llm: use top-level llm.provider'); + expect(out).toContain('Unsupported ingest.llm: unknown field'); }); it('emits structured JSON issues when validation fails', async () => { diff --git a/packages/cli/src/ingest.test.ts b/packages/cli/src/ingest.test.ts index 33eecfba..15c71e00 100644 --- a/packages/cli/src/ingest.test.ts +++ b/packages/cli/src/ingest.test.ts @@ -1471,10 +1471,10 @@ describe('runKtxIngest', () => { ' warehouse:', ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - ' minExecutions: 2', + ' context:', + ' queryHistory:', + ' enabled: true', + ' minExecutions: 2', 'ingest:', ' adapters:', ' - historic-sql', @@ -1535,10 +1535,10 @@ describe('runKtxIngest', () => { ' warehouse:', ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - ' minExecutions: 2', + ' context:', + ' queryHistory:', + ' enabled: true', + ' minExecutions: 2', 'ingest:', ' adapters:', ' - historic-sql', @@ -1663,10 +1663,10 @@ describe('runKtxIngest', () => { ' warehouse:', ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - ' minExecutions: 2', + ' context:', + ' queryHistory:', + ' enabled: true', + ' minExecutions: 2', 'ingest:', ' adapters:', ' - historic-sql', @@ -1755,10 +1755,10 @@ describe('runKtxIngest', () => { ' warehouse:', ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - ' minExecutions: 2', + ' context:', + ' queryHistory:', + ' enabled: true', + ' minExecutions: 2', 'ingest:', ' adapters:', ' - historic-sql', diff --git a/packages/cli/src/local-adapters.test.ts b/packages/cli/src/local-adapters.test.ts index efad86b6..a4e856b4 100644 --- a/packages/cli/src/local-adapters.test.ts +++ b/packages/cli/src/local-adapters.test.ts @@ -39,36 +39,6 @@ describe('CLI local ingest adapters', () => { await rm(tempDir, { recursive: true, force: true }); }); - it('registers Postgres historic SQL from the requested connection', async () => { - await writeProject( - tempDir, - [ - 'connections:', - ' warehouse:', - ' driver: postgres', - ' url: env:WAREHOUSE_DATABASE_URL', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - 'ingest:', - ' adapters:', - ' - historic-sql', - '', - ].join('\n'), - ); - const project = await loadKtxProject({ projectDir: tempDir }); - - const adapters = createKtxCliLocalIngestAdapters(project, { - historicSqlConnectionId: 'warehouse', - sqlAnalysis: sqlAnalysisStub(), - }); - - expect(adapters.find((adapter) => adapter.source === 'historic-sql')?.skillNames).toEqual([ - 'historic_sql_table_digest', - 'historic_sql_patterns', - ]); - }); - it('registers Postgres historic SQL from connection context query history', async () => { await writeProject( tempDir, @@ -110,9 +80,9 @@ describe('CLI local ingest adapters', () => { ' dataset_id: analytics', ' location: us', ' credentials_json: \'{"project_id":"demo-project"}\'', - ' historicSql:', - ' enabled: true', - ' dialect: bigquery', + ' context:', + ' queryHistory:', + ' enabled: true', 'ingest:', ' adapters:', ' - historic-sql', @@ -145,9 +115,9 @@ describe('CLI local ingest adapters', () => { ' schema_name: PUBLIC', ' username: reader', ' password: env:SNOWFLAKE_PASSWORD', - ' historicSql:', - ' enabled: true', - ' dialect: snowflake', + ' context:', + ' queryHistory:', + ' enabled: true', 'ingest:', ' adapters:', ' - historic-sql', @@ -179,9 +149,9 @@ describe('CLI local ingest adapters', () => { ' dataset_id: analytics', ' location: us', ` credentials_json: 'file:${credentialsPath}'`, - ' historicSql:', - ' enabled: true', - ' dialect: bigquery', + ' context:', + ' queryHistory:', + ' enabled: true', 'ingest:', ' adapters:', ' - historic-sql', diff --git a/packages/cli/src/setup-databases.test.ts b/packages/cli/src/setup-databases.test.ts index 50a1c6ed..a9da0f51 100644 --- a/packages/cli/src/setup-databases.test.ts +++ b/packages/cli/src/setup-databases.test.ts @@ -2125,7 +2125,6 @@ describe('setup databases step', () => { }, }, }); - expect(config.connections.snowflake.historicSql).toBeUndefined(); expect(configText).not.toContain('live-database'); expect(configText).not.toContain('historic-sql'); expect(configText).not.toMatch(/^\s+adapters:/m); @@ -2223,7 +2222,6 @@ describe('setup databases step', () => { }, }, }); - expect(config.connections.warehouse.historicSql).toBeUndefined(); const warehouseContext = config.connections.warehouse.context && typeof config.connections.warehouse.context === 'object' && @@ -2368,7 +2366,6 @@ describe('setup databases step', () => { }, }, }); - expect(config.connections.analytics.historicSql).toBeUndefined(); expect(configText).not.toContain('live-database'); expect(configText).not.toContain('historic-sql'); expect(configText).not.toMatch(/^\s+adapters:/m); @@ -2420,80 +2417,6 @@ describe('setup databases step', () => { }, }, }); - expect(config.connections.warehouse.historicSql).toBeUndefined(); - }); - - it('migrates legacy historicSql to context.queryHistory during database setup', async () => { - await writeFile( - join(tempDir, 'ktx.yaml'), - [ - 'connections:', - ' warehouse:', - ' driver: postgres', - ' readonly: true', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', - ' windowDays: 45', - ' minExecutions: 9', - ' concurrency: 3', - ' staleArchiveAfterDays: 120', - ' filters:', - ' dropTrivialProbes: true', - ' serviceAccounts:', - ' mode: exclude', - ' patterns:', - " - '^svc_'", - ' orchestrators:', - ' mode: exclude', - ' patterns:', - ' - airflow', - ' dropFailedBelow: 2', - ' redactionPatterns:', - " - '(?i)secret'", - '', - ].join('\n'), - 'utf-8', - ); - - const io = makeIo(); - - await expect( - runKtxSetupDatabasesStep( - { - projectDir: tempDir, - inputMode: 'disabled', - databaseConnectionIds: ['warehouse'], - databaseSchemas: [], - skipDatabases: false, - }, - io.io, - { - testConnection: vi.fn(async () => 0), - scanConnection: vi.fn(async () => 0), - historicSqlProbe: vi.fn(async () => ({ ok: true, lines: [] })), - }, - ), - ).resolves.toMatchObject({ status: 'ready' }); - - const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8')); - expect(config.connections.warehouse.historicSql).toBeUndefined(); - expect(config.connections.warehouse.context).toMatchObject({ - queryHistory: { - enabled: true, - windowDays: 45, - minExecutions: 9, - concurrency: 3, - staleArchiveAfterDays: 120, - filters: { - dropTrivialProbes: true, - serviceAccounts: { mode: 'exclude', patterns: ['^svc_'] }, - orchestrators: { mode: 'exclude', patterns: ['airflow'] }, - dropFailedBelow: 2, - }, - redactionPatterns: ['(?i)secret'], - }, - }); }); it('prints a non-blocking Postgres query history probe failure after connection test succeeds', async () => { diff --git a/packages/cli/src/setup-databases.ts b/packages/cli/src/setup-databases.ts index 30d4fa20..392c4761 100644 --- a/packages/cli/src/setup-databases.ts +++ b/packages/cli/src/setup-databases.ts @@ -285,13 +285,6 @@ function numberConfigField(connection: KtxProjectConnectionConfig | undefined, f return typeof value === 'number' && Number.isFinite(value) ? value : undefined; } -function historicSqlConfigRecord(connection: KtxProjectConnectionConfig | undefined): Record | null { - const historicSql = connection?.historicSql; - return historicSql && typeof historicSql === 'object' && !Array.isArray(historicSql) - ? (historicSql as Record) - : null; -} - function contextRecord(connection: KtxProjectConnectionConfig | undefined): Record { const context = connection?.context; return context && typeof context === 'object' && !Array.isArray(context) ? (context as Record) : {}; @@ -304,19 +297,12 @@ function queryHistoryConfigRecord(connection: KtxProjectConnectionConfig | undef : null; } -function stripLegacyHistoricSql(connection: KtxProjectConnectionConfig): KtxProjectConnectionConfig { - const { historicSql: _historicSql, ...rest } = connection as KtxProjectConnectionConfig & { - historicSql?: unknown; - }; - return rest; -} - function withQueryHistoryConfig( connection: KtxProjectConnectionConfig, queryHistory: Record, ): KtxProjectConnectionConfig { return { - ...stripLegacyHistoricSql(connection), + ...connection, context: { ...contextRecord(connection), queryHistory, @@ -324,16 +310,6 @@ function withQueryHistoryConfig( }; } -function migrateLegacyHistoricSqlConnection(connection: KtxProjectConnectionConfig): KtxProjectConnectionConfig { - const existingQueryHistory = queryHistoryConfigRecord(connection); - const legacy = historicSqlConfigRecord(connection); - if (existingQueryHistory || !legacy) { - return existingQueryHistory ? stripLegacyHistoricSql(connection) : connection; - } - const { dialect: _dialect, ...queryHistory } = legacy; - return withQueryHistoryConfig(connection, queryHistory); -} - function historicSqlProbeFailureLines(error: unknown): string[] { if (error instanceof Error && error.name === 'HistoricSqlExtensionMissingError') { return [ @@ -1133,8 +1109,7 @@ async function maybeApplyHistoricSqlConfig(input: { } } - const existingRecord = queryHistoryConfigRecord(input.connection) ?? historicSqlConfigRecord(input.connection) ?? {}; - const { dialect: _dialect, ...existing } = existingRecord; + const { dialect: _dialect, ...existing } = queryHistoryConfigRecord(input.connection) ?? {}; if (!enabled) { return withQueryHistoryConfig(input.connection, { ...existing, enabled: false }); @@ -1386,18 +1361,11 @@ async function writeConnectionConfig(input: { io?: KtxCliIo; }): Promise { const project = await loadKtxProject({ projectDir: input.projectDir }); - const migratedConnections = Object.fromEntries( - Object.entries(project.config.connections).map(([connectionId, connection]) => [ - connectionId, - migrateLegacyHistoricSqlConnection(connection), - ]), - ); - const nextConnection = migrateLegacyHistoricSqlConnection(input.connection); const config = { ...project.config, connections: { - ...migratedConnections, - [input.connectionId]: nextConnection, + ...project.config.connections, + [input.connectionId]: input.connection, }, }; await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8'); @@ -1407,13 +1375,13 @@ async function writeConnectionConfig(input: { projectDir: input.projectDir, io: input.io, fields: { - driver: String(nextConnection.driver ?? 'unknown').toLowerCase(), - isDemoConnection: isDemoConnection(input.connectionId, nextConnection), + driver: String(input.connection.driver ?? 'unknown').toLowerCase(), + isDemoConnection: isDemoConnection(input.connectionId, input.connection), }, }); } - const queryHistory = queryHistoryConfigRecord(nextConnection); + const queryHistory = queryHistoryConfigRecord(input.connection); if (queryHistory?.enabled === true) { await ensureHistoricSqlIngestDefaults(input.projectDir); } @@ -1745,18 +1713,7 @@ async function ensureHistoricSqlIngestDefaults(projectDir: string): Promise { const project = await loadKtxProject({ projectDir }); - const config = setKtxSetupDatabaseConnectionIds( - { - ...project.config, - connections: Object.fromEntries( - Object.entries(project.config.connections).map(([connectionId, connection]) => [ - connectionId, - migrateLegacyHistoricSqlConnection(connection), - ]), - ), - }, - unique(connectionIds), - ); + const config = setKtxSetupDatabaseConnectionIds(project.config, unique(connectionIds)); await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8'); await markKtxSetupStateStepComplete(projectDir, 'databases'); } @@ -1769,7 +1726,7 @@ async function maybeRunHistoricSqlSetupProbe(input: { }): Promise { const project = await loadKtxProject({ projectDir: input.projectDir }); const connection = project.config.connections[input.connectionId]; - const queryHistory = queryHistoryConfigRecord(connection) ?? historicSqlConfigRecord(connection); + const queryHistory = queryHistoryConfigRecord(connection); const driver = normalizeDriver(connection?.driver); if (queryHistory?.enabled !== true) { return; @@ -2133,12 +2090,7 @@ async function runPrimarySourceFullEdit(input: { connectionId: input.connectionId, connection: withExistingPrimaryEditPromptDefaults({ previous: existing, - next: { - ...withHistoricSql, - ...(!Object.hasOwn(withHistoricSql, 'historicSql') && existing.historicSql !== undefined - ? { historicSql: existing.historicSql } - : {}), - }, + next: withHistoricSql, driver, }), io: input.io, diff --git a/packages/cli/src/setup-project.test.ts b/packages/cli/src/setup-project.test.ts index 26b1e35b..89663bbf 100644 --- a/packages/cli/src/setup-project.test.ts +++ b/packages/cli/src/setup-project.test.ts @@ -1,8 +1,6 @@ import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { initKtxProject } from './context/project/project.js'; -import { parseKtxProjectConfig } from './context/project/config.js'; import { readKtxSetupState } from './context/project/setup-config.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { gray } from './io/symbols.js'; @@ -76,36 +74,6 @@ describe('setup project step', () => { expect(testIo.stderr()).toBe(''); }); - it('loads an existing project in auto mode and drops config setup progress', async () => { - const projectDir = join(tempDir, 'warehouse'); - await initKtxProject({ projectDir }); - await writeFile( - join(projectDir, 'ktx.yaml'), - [ - 'setup:', - ' database_connection_ids:', - ' - warehouse', - ' completed_steps:', - ' - llm', - 'connections: {}', - ].join('\n'), - 'utf-8', - ); - - const result = await runKtxSetupProjectStep( - { projectDir, mode: 'auto', inputMode: 'disabled', yes: false }, - makeIo().io, - ); - - expect(result.status).toBe('ready'); - const config = parseKtxProjectConfig(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8')); - expect(config.setup).toEqual({ - database_connection_ids: ['warehouse'], - }); - expect(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:'); - expect(await readKtxSetupState(projectDir)).toEqual({ completed_steps: ['project'] }); - }); - it('creates a missing auto-mode project only when --yes is present in no-input mode', async () => { const projectDir = join(tempDir, 'warehouse'); const rejectedIo = makeIo(); diff --git a/packages/cli/src/status-project.ts b/packages/cli/src/status-project.ts index 9b5f1af4..aaecff27 100644 --- a/packages/cli/src/status-project.ts +++ b/packages/cli/src/status-project.ts @@ -92,10 +92,6 @@ type ClaudeCodeAuthProbe = (input: { const PROJECT_READY_COMMANDS = KTX_NEXT_STEP_DIRECT_COMMANDS.map((step) => step.command); -function isRecord(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - function hasOwnField(value: Record, key: string): boolean { return Object.prototype.hasOwnProperty.call(value, key); } @@ -766,27 +762,6 @@ function buildWarnings( fix: 'Remove it from ktx.yaml. KTX stores the Notion cursor in .ktx/db.sqlite.', }); } - - const historicSql = isRecord(connection.historicSql) ? connection.historicSql : null; - if (!historicSql) { - continue; - } - if (hasOwnField(historicSql, 'concurrency')) { - warnings.push({ - message: `connections.${connectionId}.historicSql.concurrency is no longer used.`, - fix: `Remove connections.${connectionId}.historicSql.concurrency from ktx.yaml.`, - }); - } - const historicDialect = String(historicSql.dialect ?? driver).toLowerCase(); - if ( - (historicDialect === 'postgres' || historicDialect === 'postgresql') && - hasOwnField(historicSql, 'windowDays') - ) { - warnings.push({ - message: `connections.${connectionId}.historicSql.windowDays does not constrain pg_stat_statements.`, - fix: `Remove connections.${connectionId}.historicSql.windowDays from ktx.yaml.`, - }); - } } for (const adapter of config.ingest.adapters) {