refactor: remove legacy compatibility shims (#208)

This commit is contained in:
Andrey Avtomonov 2026-05-24 01:00:20 +02:00 committed by GitHub
parent 394a985d2a
commit db09936085
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 61 additions and 361 deletions

View file

@ -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');
});

View file

@ -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;
}

View file

@ -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<string, unknown> | null {
return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
}
function historicSqlRecord(connection: unknown): Record<string, unknown> | null {
const conn = recordOrNull(connection);
return conn ? recordOrNull(conn.historicSql) : null;
}
function queryHistoryRecord(connection: unknown): Record<string, unknown> | null {
const conn = recordOrNull(connection);
const context = conn ? recordOrNull(conn.context) : null;
@ -22,11 +11,7 @@ function queryHistoryRecord(connection: unknown): Record<string, unknown> | 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;
}

View file

@ -152,10 +152,10 @@ async function writeHistoricSqlProject(project: KtxLocalProject): Promise<KtxLoc
'connections:',
' warehouse:',
' driver: postgres',
' historicSql:',
' enabled: true',
' dialect: postgres',
' minExecutions: 2',
' context:',
' queryHistory:',
' enabled: true',
' minExecutions: 2',
'ingest:',
' adapters:',
' - historic-sql',

View file

@ -187,14 +187,15 @@ describe('local ingest adapters', () => {
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: {

View file

@ -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);

View file

@ -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);

View file

@ -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<string, string> = {
'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` };
});
}

View file

@ -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 } },
});
});

View file

@ -35,7 +35,7 @@ function warehouseConnectionSchema<const Driver extends WarehouseDriver>(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.`,
);
}

View file

@ -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 () => {

View file

@ -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',

View file

@ -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',

View file

@ -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 () => {

View file

@ -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<string, unknown> | null {
const historicSql = connection?.historicSql;
return historicSql && typeof historicSql === 'object' && !Array.isArray(historicSql)
? (historicSql as Record<string, unknown>)
: null;
}
function contextRecord(connection: KtxProjectConnectionConfig | undefined): Record<string, unknown> {
const context = connection?.context;
return context && typeof context === 'object' && !Array.isArray(context) ? (context as Record<string, unknown>) : {};
@ -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<string, unknown>,
): 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<void> {
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<void
async function markDatabasesComplete(projectDir: string, connectionIds: string[]): Promise<void> {
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<void> {
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,

View file

@ -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();

View file

@ -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<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function hasOwnField(value: Record<string, unknown>, 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) {