mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-22 08:38:08 +02:00
refactor: enforce ktx naming and AGENTS.md compliance sweep (#289)
Align the tree with AGENTS.md/CLAUDE.md conventions: - Rewrite user-facing strings, docs, and tests to lowercase `ktx` (no bare uppercase `KTX` tokens remain outside literal identifiers). - Drop the legacy `historicSql` migration path and its now-unused helpers, per the no-backward-compat rule. - Remove `as unknown as` / `any` casts: narrow `BaseTool` generics to `z.ZodObject`, add a typed `createLookerClient`, and delete the dead `getParametersSchema`/`toAnthropicFormat` pre-AI-SDK helpers. - Use `InvalidArgumentError` for Commander parse failures. - Finish the adapter→connector prose conversion in the `ktx.yaml` docs while keeping the literal `adapters` config key.
This commit is contained in:
parent
005c5fc860
commit
00cdf2de90
237 changed files with 844 additions and 974 deletions
|
|
@ -4,7 +4,7 @@ import { join } from 'node:path';
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { resolveKtxConfigReference, resolveKtxHomePath } from '../../../src/context/core/config-reference.js';
|
||||
|
||||
describe('KTX config references', () => {
|
||||
describe('ktx config references', () => {
|
||||
it('resolves env references without returning empty values', () => {
|
||||
expect(resolveKtxConfigReference('env:AI_GATEWAY_API_KEY', { AI_GATEWAY_API_KEY: ' gateway-key ' })).toBe(
|
||||
'gateway-key',
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ async function writeHistoricSqlProject(project: KtxLocalProject): Promise<KtxLoc
|
|||
' state: sqlite',
|
||||
' search: sqlite-fts5',
|
||||
' git:',
|
||||
' author: KTX Test <system@ktx.local>',
|
||||
' author: ktx Test <system@ktx.local>',
|
||||
'',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
|
|
@ -193,7 +193,7 @@ async function writeHistoricSqlProject(project: KtxLocalProject): Promise<KtxLoc
|
|||
},
|
||||
},
|
||||
}),
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
'Seed schema shard',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ function sdk(): LookerSdkPort {
|
|||
}
|
||||
|
||||
describe('DefaultLookerConnectionClientFactory', () => {
|
||||
it('resolves credentials by Looker connection id and creates a KTX Looker client', async () => {
|
||||
it('resolves credentials by Looker connection id and creates a ktx Looker client', async () => {
|
||||
const fakeSdk = sdk();
|
||||
const resolver: LookerCredentialResolver = {
|
||||
resolve: vi.fn().mockResolvedValue({
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ describe('discoverLookerConnections', () => {
|
|||
});
|
||||
|
||||
describe('looker dialect and target validation helpers', () => {
|
||||
it('maps Looker dialect names to KTX connection types', () => {
|
||||
it('maps Looker dialect names to ktx connection types', () => {
|
||||
expect(lookerDialectToConnectionType('bigquery_standard_sql')).toBe('BIGQUERY');
|
||||
expect(lookerDialectToConnectionType('postgres')).toBe('POSTGRESQL');
|
||||
expect(lookerDialectToConnectionType('mssql')).toBeNull();
|
||||
|
|
@ -224,7 +224,7 @@ describe('computeLookerMappingDrift and validateLookerMappings', () => {
|
|||
).toEqual({
|
||||
ok: false,
|
||||
errors: [
|
||||
{ key: 'b2b_sandbox_bq', reason: 'KTX connection missing does not exist' },
|
||||
{ key: 'b2b_sandbox_bq', reason: 'ktx connection missing does not exist' },
|
||||
{
|
||||
key: 'pg_runtime',
|
||||
reason: 'Connection type LOOKER cannot be used as a Looker warehouse mapping target',
|
||||
|
|
@ -259,7 +259,7 @@ describe('collectExploreParseItems and projectParsedIdentifier', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('projects successful and failed parser rows into KTX parsed target tables', () => {
|
||||
it('projects successful and failed parser rows into ktx parsed target tables', () => {
|
||||
expect(
|
||||
projectParsedIdentifier({
|
||||
ok: true,
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ describe('Looker staged runtime schemas', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('accepts slug-shaped connection ids inside KTX Looker runtime schemas', () => {
|
||||
it('accepts slug-shaped connection ids inside ktx Looker runtime schemas', () => {
|
||||
const parsedTargetTable = {
|
||||
ok: true as const,
|
||||
catalog: 'proj',
|
||||
|
|
@ -313,7 +313,7 @@ describe('Looker staged runtime schemas', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('rejects unsafe KTX Looker connection ids', () => {
|
||||
it('rejects unsafe ktx Looker connection ids', () => {
|
||||
expect(() =>
|
||||
parseLookerPullConfig({
|
||||
lookerConnectionId: '../prod-looker',
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ async function readMetabaseFile(name: string): Promise<string> {
|
|||
return readFile(join(metabaseDir, name), 'utf-8');
|
||||
}
|
||||
|
||||
describe('KTX Metabase client boundary', () => {
|
||||
it('keeps NestJS, server data-source base classes, and server-relative imports out of the KTX client', async () => {
|
||||
describe('ktx Metabase client boundary', () => {
|
||||
it('keeps NestJS, server data-source base classes, and server-relative imports out of the ktx client', async () => {
|
||||
const client = await readMetabaseFile('client.ts');
|
||||
expect(client).not.toContain(`@${'nestjs'}`);
|
||||
expect(client).not.toContain(`DataSource${'Client'}`);
|
||||
|
|
@ -19,7 +19,7 @@ describe('KTX Metabase client boundary', () => {
|
|||
expect(client).not.toContain('../../types/brand');
|
||||
});
|
||||
|
||||
it('keeps proxy implementation code out of the KTX v1 client', async () => {
|
||||
it('keeps proxy implementation code out of the ktx v1 client', async () => {
|
||||
const client = await readMetabaseFile('client.ts');
|
||||
expect(client).not.toContain(`network-${'proxy'}`);
|
||||
expect(client).not.toContain(`ssh${'2'}`);
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ describe('MetabaseClient admin auth helpers', () => {
|
|||
);
|
||||
|
||||
await expect(client.getPermissionGroups()).resolves.toEqual([{ id: 2, name: 'Administrators' }]);
|
||||
await expect(client.createApiKey({ name: 'KTX CLI test', groupId: 2 })).resolves.toBe(mintedMetabaseCredential);
|
||||
await expect(client.createApiKey({ name: 'ktx CLI test', groupId: 2 })).resolves.toBe(mintedMetabaseCredential);
|
||||
|
||||
expect(fetchMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
|
|
@ -247,7 +247,7 @@ describe('MetabaseClient admin auth helpers', () => {
|
|||
'https://metabase.example.test/api/api-key',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ name: 'KTX CLI test', group_id: 2 }),
|
||||
body: JSON.stringify({ name: 'ktx CLI test', group_id: 2 }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ describe('metabaseRuntimeConfigFromLocalConnection', () => {
|
|||
};
|
||||
|
||||
expect(() => metabaseRuntimeConfigFromLocalConnection('prod-metabase', connection)).toThrow(
|
||||
'Standalone KTX does not support proxy-bearing Metabase connections yet',
|
||||
'Standalone ktx does not support proxy-bearing Metabase connections yet',
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ describe('validateMetabaseMappings', () => {
|
|||
}),
|
||||
).toEqual({
|
||||
ok: false,
|
||||
errors: [{ key: '2', reason: 'KTX connection missing-target does not exist' }],
|
||||
errors: [{ key: '2', reason: 'ktx connection missing-target does not exist' }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -149,7 +149,7 @@ describe('validateMappingPhysicalMatch', () => {
|
|||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for unknown engines because KTX cannot validate them', () => {
|
||||
it('returns null for unknown engines because ktx cannot validate them', () => {
|
||||
expect(
|
||||
validateMappingPhysicalMatch(
|
||||
{ metabaseEngine: 'unknown-engine', metabaseDbName: 'X', metabaseHost: 'host' },
|
||||
|
|
@ -177,7 +177,7 @@ describe('computeMetabaseMappingPhysicalMismatches', () => {
|
|||
).toEqual([
|
||||
{
|
||||
mappingId: 'mapping-bad',
|
||||
reason: "Metabase database 'app' does not match KTX connection database 'other_app'",
|
||||
reason: "Metabase database 'app' does not match ktx connection database 'other_app'",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
@ -216,7 +216,7 @@ describe('refreshMetabaseMapping', () => {
|
|||
physicalMismatches: [
|
||||
{
|
||||
mappingId: '2',
|
||||
reason: "Metabase database 'analytics' does not match KTX connection database 'wrong_database'",
|
||||
reason: "Metabase database 'analytics' does not match ktx connection database 'wrong_database'",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -282,7 +282,7 @@ describe('findBestMatch', () => {
|
|||
});
|
||||
|
||||
describe('METABASE_ENGINE_TO_CONNECTION_TYPE', () => {
|
||||
it('keeps the server-supported Metabase engine table in KTX', () => {
|
||||
it('keeps the server-supported Metabase engine table in ktx', () => {
|
||||
expect(METABASE_ENGINE_TO_CONNECTION_TYPE).toMatchObject({
|
||||
postgres: 'POSTGRESQL',
|
||||
bigquery: 'BIGQUERY',
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ function makeDeps(
|
|||
lockingService: { withLock: vi.fn(async (_key, fn) => fn()) },
|
||||
storage: {
|
||||
homeDir: join(runtime.configDir, '.ktx'),
|
||||
systemGitAuthor: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
systemGitAuthor: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
resolveUploadDir: (id) => join(runtime.homeDir, 'upload', id),
|
||||
resolvePullDir: (id) => join(runtime.homeDir, 'pull', id),
|
||||
resolveTranscriptDir: (id) => join(runtime.configDir, '.ktx/ingest-transcripts', id),
|
||||
|
|
@ -308,7 +308,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['wiki/global/custom-isolated.md'],
|
||||
'custom wiki',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -395,7 +395,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['wiki/global/legacy-isolated.md'],
|
||||
'legacy isolated wiki',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -486,7 +486,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/good.yaml'],
|
||||
'test: add good source',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
}
|
||||
|
|
@ -504,7 +504,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/bad.yaml'],
|
||||
'test: add bad source',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
}
|
||||
|
|
@ -606,7 +606,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
[`wiki/global/${sourceKey}-isolated.md`],
|
||||
`${sourceKey} wiki`,
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -682,7 +682,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
'---\nsummary: Account segments\nusage_mode: auto\nsl_refs:\n - mart_account_segments\n---\n\nARR is `mart_account_segments.total_contract_arr_cents`.\n',
|
||||
);
|
||||
currentSession.actions.push({ target: 'wiki', type: 'created', key: 'account-segments', detail: 'Account segments' });
|
||||
await currentSession.gitService.commitFiles(['wiki/global/account-segments.md'], 'wu wiki', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['wiki/global/account-segments.md'], 'wu wiki', 'ktx Test', 'system@ktx.local');
|
||||
}
|
||||
if (params.telemetryTags.unitKey === 'card-source') {
|
||||
await writeFile(
|
||||
|
|
@ -697,7 +697,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
detail: 'Dollar measure',
|
||||
targetConnectionId: 'warehouse',
|
||||
});
|
||||
await currentSession.gitService.commitFiles(['semantic-layer/warehouse/mart_account_segments.yaml'], 'wu source', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['semantic-layer/warehouse/mart_account_segments.yaml'], 'wu source', 'ktx Test', 'system@ktx.local');
|
||||
}
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
|
|
@ -740,7 +740,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await runtime.git.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml', 'wiki/global/account-segments.md'],
|
||||
'seed existing wiki body ref',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
const preRunHead = await runtime.git.revParseHead();
|
||||
|
|
@ -773,7 +773,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml'],
|
||||
'wu source rename',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -903,7 +903,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await mkdir(join(root, 'wiki/global'), { recursive: true });
|
||||
await writeFile(join(root, `wiki/global/${unitKey}.md`), `---\nsummary: ${unitKey}\nusage_mode: auto\n---\n\n${unitKey}\n`);
|
||||
currentSession.actions.push({ target: 'wiki', type: 'created', key: unitKey, detail: unitKey });
|
||||
await currentSession.gitService.commitFiles([`wiki/global/${unitKey}.md`], `wu ${unitKey}`, 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles([`wiki/global/${unitKey}.md`], `wu ${unitKey}`, 'ktx Test', 'system@ktx.local');
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
const runner = new IngestBundleRunner(deps);
|
||||
|
|
@ -950,7 +950,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
);
|
||||
addTouchedSlSource(currentSession.touchedSlSources, 'warehouse', 'orders');
|
||||
currentSession.actions.push({ target: 'sl', type: 'updated', key: 'orders', detail: suffix, targetConnectionId: 'warehouse' });
|
||||
await currentSession.gitService.commitFiles(['semantic-layer/warehouse/orders.yaml'], `wu ${suffix}`, 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['semantic-layer/warehouse/orders.yaml'], `wu ${suffix}`, 'ktx Test', 'system@ktx.local');
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
const runner = new IngestBundleRunner(deps);
|
||||
|
|
@ -1006,7 +1006,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
'---\nsummary: Projected orders\nusage_mode: auto\nsl_refs:\n - mart_account_segments\n---\n\nARR `mart_account_segments.total_contract_arr`.\n',
|
||||
);
|
||||
currentSession.actions.push({ target: 'wiki', type: 'created', key: 'projected-orders', detail: 'Projected orders' });
|
||||
await currentSession.gitService.commitFiles(['wiki/global/projected-orders.md'], 'wu projected wiki', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['wiki/global/projected-orders.md'], 'wu projected wiki', 'ktx Test', 'system@ktx.local');
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
const runner = new IngestBundleRunner(deps);
|
||||
|
|
@ -1042,7 +1042,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await mkdir(join(root, 'wiki/global'), { recursive: true });
|
||||
await writeFile(join(root, 'wiki/global/notion-page.md'), '---\nsummary: Notion page\nusage_mode: auto\nsl_refs:\n - missing_source\n---\n\nBody\n');
|
||||
currentSession.actions.push({ target: 'wiki', type: 'created', key: 'notion-page', detail: 'Notion page' });
|
||||
await currentSession.gitService.commitFiles(['wiki/global/notion-page.md'], 'wu notion', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['wiki/global/notion-page.md'], 'wu notion', 'ktx Test', 'system@ktx.local');
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
const runner = new IngestBundleRunner(deps);
|
||||
|
|
@ -1088,7 +1088,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml'],
|
||||
'wu source',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
} else {
|
||||
|
|
@ -1104,7 +1104,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
detail: 'Stale reconcile wiki page',
|
||||
rawPaths: ['cards/source.json'],
|
||||
});
|
||||
await currentSession.gitService.commitFiles(['wiki/global/account-segments.md'], 'reconcile wiki', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['wiki/global/account-segments.md'], 'reconcile wiki', 'ktx Test', 'system@ktx.local');
|
||||
}
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
|
|
@ -1167,7 +1167,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
detail: 'Account segments',
|
||||
rawPaths: ['cards/wiki.json'],
|
||||
});
|
||||
await currentSession.gitService.commitFiles(['wiki/global/account-segments.md'], 'wu wiki', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['wiki/global/account-segments.md'], 'wu wiki', 'ktx Test', 'system@ktx.local');
|
||||
}
|
||||
if (params.telemetryTags.unitKey === 'card-source') {
|
||||
await mkdir(join(root, 'semantic-layer/warehouse'), { recursive: true });
|
||||
|
|
@ -1187,7 +1187,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml'],
|
||||
'wu source',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
}
|
||||
|
|
@ -1302,7 +1302,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml', 'wiki/global/account-segments.md'],
|
||||
'valid artifacts with invalid provenance',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -1444,7 +1444,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
'name: orders\ngrain: [id]\ncolumns: [{name: id, type: string}]\njoins: []\nmeasures: []\n',
|
||||
);
|
||||
currentSession.actions.push({ target: 'sl', type: 'created', key: 'orders', detail: 'forbidden', targetConnectionId: 'warehouse' });
|
||||
await currentSession.gitService.commitFiles(['semantic-layer/warehouse/orders.yaml'], 'forbidden sl', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['semantic-layer/warehouse/orders.yaml'], 'forbidden sl', 'ktx Test', 'system@ktx.local');
|
||||
return { stopReason: 'natural' };
|
||||
}) as never;
|
||||
const runner = new IngestBundleRunner(deps);
|
||||
|
|
@ -1469,7 +1469,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
join(runtime.configDir, 'wiki/global/source-page.md'),
|
||||
'---\nsummary: Source page\nusage_mode: auto\n---\n\nSource page\n',
|
||||
);
|
||||
await runtime.git.commitFiles(['wiki/global/source-page.md'], 'seed source page', 'KTX Test', 'system@ktx.local');
|
||||
await runtime.git.commitFiles(['wiki/global/source-page.md'], 'seed source page', 'ktx Test', 'system@ktx.local');
|
||||
const preRunHead = await runtime.git.revParseHead();
|
||||
const { deps, adapter } = makeDeps(runtime);
|
||||
adapter.chunk.mockResolvedValue({
|
||||
|
|
@ -1501,7 +1501,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['wiki/global/account-segments.md'],
|
||||
'wu page ref',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
}
|
||||
|
|
@ -1517,7 +1517,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['wiki/global/source-page.md'],
|
||||
'wu delete source page',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
}
|
||||
|
|
@ -1582,7 +1582,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await runtime.git.commitFiles(
|
||||
['wiki/global/source-page.md', 'wiki/global/account-segments.md'],
|
||||
'seed inbound wiki refs',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
const preRunHead = await runtime.git.revParseHead();
|
||||
|
|
@ -1613,7 +1613,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['wiki/global/source-page.md'],
|
||||
'wu delete target page',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -1751,7 +1751,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/finance/orders.yaml'],
|
||||
'wu unauthorized target',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -1822,7 +1822,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
detail: 'Valid page',
|
||||
rawPaths: ['pages/source.json'],
|
||||
});
|
||||
await currentSession.gitService.commitFiles(['wiki/global/valid-page.md'], 'wu valid page', 'KTX Test', 'system@ktx.local');
|
||||
await currentSession.gitService.commitFiles(['wiki/global/valid-page.md'], 'wu valid page', 'ktx Test', 'system@ktx.local');
|
||||
} else {
|
||||
await mkdir(join(root, 'semantic-layer/finance'), { recursive: true });
|
||||
await writeFile(
|
||||
|
|
@ -1841,7 +1841,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/finance/reconcile_orders.yaml'],
|
||||
'reconcile unauthorized target',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
}
|
||||
|
|
@ -1959,7 +1959,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml'],
|
||||
`wu ${params.telemetryTags.unitKey}`,
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' };
|
||||
|
|
@ -2025,7 +2025,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await runtime.git.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml', 'wiki/global/account-segments.md'],
|
||||
'seed stale wiki body ref',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
|
||||
|
|
@ -2073,7 +2073,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml'],
|
||||
'wu source rename',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' as const };
|
||||
|
|
@ -2128,7 +2128,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await runtime.git.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml', 'wiki/global/account-segments.md'],
|
||||
'seed stale wiki body ref',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
const preRunHead = await runtime.git.revParseHead();
|
||||
|
|
@ -2168,7 +2168,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['semantic-layer/warehouse/mart_account_segments.yaml'],
|
||||
'wu source rename',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' as const };
|
||||
|
|
@ -2302,7 +2302,7 @@ describe('IngestBundleRunner isolated diff path', () => {
|
|||
await currentSession.gitService.commitFiles(
|
||||
['wiki/global/orders.md'],
|
||||
'wu orders',
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
);
|
||||
return { stopReason: 'natural' as const };
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ const buildRunner = (deps: ReturnType<typeof makeDeps> = makeDeps(), overrides:
|
|||
lockingService: deps.lockingService as any,
|
||||
storage: {
|
||||
homeDir: '/tmp/ktx-test',
|
||||
systemGitAuthor: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
systemGitAuthor: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
resolveUploadDir: (uploadId) => `/tmp/ktx-test/ingest-uploads/${uploadId}`,
|
||||
resolvePullDir: (jobId) => `/tmp/ktx-test/ingest-pulls/${jobId}`,
|
||||
resolveTranscriptDir: (jobId) => `/tmp/ktx-test/run/wu-transcripts/${jobId}`,
|
||||
|
|
@ -1518,7 +1518,7 @@ describe('IngestBundleRunner — Stages 1 → 7', () => {
|
|||
...(buildRunner(deps) as any).deps,
|
||||
storage: {
|
||||
homeDir: tempRoot,
|
||||
systemGitAuthor: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
systemGitAuthor: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
resolveUploadDir: (uploadId: string) => join(tempRoot, 'ingest-uploads', uploadId),
|
||||
resolvePullDir: (jobId: string) => join(tempRoot, 'ingest-pulls', jobId),
|
||||
resolveTranscriptDir: (jobId: string) => join(tempRoot, 'run', 'wu-transcripts', jobId),
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ describe('ingest prompt assets', () => {
|
|||
expect(prompt).toContain('Do not create a duplicate contested artifact');
|
||||
});
|
||||
|
||||
it('uses product-neutral KTX runtime wording', async () => {
|
||||
it('uses product-neutral ktx runtime wording', async () => {
|
||||
const prompt = await readFile(
|
||||
new URL('../../../src/prompts/memory_agent_bundle_ingest_work_unit.md', import.meta.url),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
expect(prompt).toContain('KTX semantic-layer sources and/or knowledge wiki pages');
|
||||
expect(prompt).toContain('maps cleanly to KTX');
|
||||
expect(prompt).toContain('ktx semantic-layer sources and/or knowledge wiki pages');
|
||||
expect(prompt).toContain('maps cleanly to ktx');
|
||||
expect(prompt).not.toMatch(forbiddenProductPattern());
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ function forbiddenProductPattern() {
|
|||
}
|
||||
|
||||
describe('ingest runtime assets', () => {
|
||||
it('resolves every reusable ingest skill from packaged KTX assets without server fallback', async () => {
|
||||
it('resolves every reusable ingest skill from packaged ktx assets without server fallback', async () => {
|
||||
const registry = new SkillsRegistryService({ skillsDir });
|
||||
const expected = [...new Set([...adapterSkillNames, ...adapterReconcileSkillNames])].sort();
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ describe('ingest runtime assets', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('loads page-triage and light-extraction prompts from packaged KTX prompt assets', async () => {
|
||||
it('loads page-triage and light-extraction prompts from packaged ktx prompt assets', async () => {
|
||||
const prompts = new PromptService({ promptsDir, partials: [] });
|
||||
|
||||
for (const promptName of pageTriagePromptNames) {
|
||||
|
|
@ -61,7 +61,7 @@ describe('ingest runtime assets', () => {
|
|||
await expect(prompts.loadPrompt('skills/light_extraction')).resolves.toContain('# Light Context Extraction');
|
||||
});
|
||||
|
||||
it('packages historic-SQL table digest guidance from KTX assets', async () => {
|
||||
it('packages historic-SQL table digest guidance from ktx assets', async () => {
|
||||
const registry = new SkillsRegistryService({ skillsDir });
|
||||
const skills = await registry.listSkills(['historic_sql_table_digest'], 'memory_agent');
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ describe('ingest runtime assets', () => {
|
|||
expect(body).not.toMatch(forbiddenProductPattern());
|
||||
});
|
||||
|
||||
it('packages historic-SQL patterns guidance from KTX assets', async () => {
|
||||
it('packages historic-SQL patterns guidance from ktx assets', async () => {
|
||||
const registry = new SkillsRegistryService({ skillsDir });
|
||||
const skills = await registry.listSkills(['historic_sql_patterns'], 'memory_agent');
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ describe('integrateWorkUnitPatch', () => {
|
|||
patchPath,
|
||||
integrationGit: git,
|
||||
trace,
|
||||
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
author: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
validateAppliedTree: vi.fn().mockResolvedValue(undefined),
|
||||
slDisallowed: false,
|
||||
allowedTargetConnectionIds: new Set(['c1']),
|
||||
|
|
@ -82,7 +82,7 @@ describe('integrateWorkUnitPatch', () => {
|
|||
patchPath,
|
||||
integrationGit: git,
|
||||
trace,
|
||||
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
author: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
validateAppliedTree: vi.fn().mockRejectedValue(new Error('final artifact gates failed')),
|
||||
slDisallowed: false,
|
||||
allowedTargetConnectionIds: new Set(['c1']),
|
||||
|
|
@ -117,7 +117,7 @@ describe('integrateWorkUnitPatch', () => {
|
|||
patchPath,
|
||||
integrationGit: git,
|
||||
trace,
|
||||
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
author: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
validateAppliedTree: vi.fn().mockResolvedValue(undefined),
|
||||
slDisallowed: true,
|
||||
allowedTargetConnectionIds: new Set(['c1']),
|
||||
|
|
@ -158,7 +158,7 @@ describe('integrateWorkUnitPatch', () => {
|
|||
patchPath,
|
||||
integrationGit: git,
|
||||
trace,
|
||||
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
author: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
validateAppliedTree: vi.fn().mockResolvedValue(undefined),
|
||||
slDisallowed: false,
|
||||
allowedTargetConnectionIds: new Set(['warehouse']),
|
||||
|
|
@ -325,7 +325,7 @@ describe('integrateWorkUnitPatch', () => {
|
|||
patchPath,
|
||||
integrationGit: git,
|
||||
trace,
|
||||
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
author: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
validateAppliedTree,
|
||||
slDisallowed: false,
|
||||
allowedTargetConnectionIds: new Set(['c1']),
|
||||
|
|
@ -380,7 +380,7 @@ describe('integrateWorkUnitPatch', () => {
|
|||
patchPath,
|
||||
integrationGit: git,
|
||||
trace,
|
||||
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
||||
author: { name: 'ktx Test', email: 'system@ktx.local' },
|
||||
validateAppliedTree: vi.fn().mockRejectedValue(new Error('final artifact gates failed')),
|
||||
slDisallowed: false,
|
||||
allowedTargetConnectionIds: new Set(['c1']),
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ describe('runIsolatedWorkUnit', () => {
|
|||
run: async (child) => {
|
||||
await mkdir(join(child.workdir, 'wiki/global'), { recursive: true });
|
||||
await writeFile(join(child.workdir, 'wiki/global/a.md'), '---\nsummary: A\nusage_mode: auto\n---\n\nBody\n');
|
||||
await child.git.commitFiles(['wiki/global/a.md'], 'test: write wiki', 'KTX Test', 'system@ktx.local');
|
||||
await child.git.commitFiles(['wiki/global/a.md'], 'test: write wiki', 'ktx Test', 'system@ktx.local');
|
||||
return {
|
||||
unitKey: 'wu-1',
|
||||
status: 'success',
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ describe('canonical local ingest', () => {
|
|||
' state: sqlite',
|
||||
' search: sqlite-fts5',
|
||||
' git:',
|
||||
' author: KTX Test <system@ktx.local>',
|
||||
' author: ktx Test <system@ktx.local>',
|
||||
'',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
|
|
@ -530,7 +530,7 @@ describe('canonical local ingest', () => {
|
|||
await historicProject.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/_schema/public.yaml',
|
||||
YAML.stringify({ tables: { orders: { table: 'public.orders', columns: [{ name: 'id', type: 'string' }] } } }),
|
||||
'KTX Test',
|
||||
'ktx Test',
|
||||
'system@ktx.local',
|
||||
'Seed schema shard',
|
||||
);
|
||||
|
|
@ -682,7 +682,7 @@ describe('canonical local ingest', () => {
|
|||
' state: sqlite',
|
||||
' search: sqlite-fts5',
|
||||
' git:',
|
||||
' author: KTX Test <system@ktx.local>',
|
||||
' author: ktx Test <system@ktx.local>',
|
||||
'',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
|
|
@ -765,7 +765,7 @@ describe('canonical local ingest', () => {
|
|||
' state: sqlite',
|
||||
' search: sqlite-fts5',
|
||||
' git:',
|
||||
' author: KTX Test <system@ktx.local>',
|
||||
' author: ktx Test <system@ktx.local>',
|
||||
'',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
|
|
@ -808,7 +808,7 @@ describe('canonical local ingest', () => {
|
|||
' state: sqlite',
|
||||
' search: sqlite-fts5',
|
||||
' git:',
|
||||
' author: KTX Test <system@ktx.local>',
|
||||
' author: ktx Test <system@ktx.local>',
|
||||
'',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
|
|
|
|||
|
|
@ -596,7 +596,7 @@ describe('local ingest', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('includes upload-capable KTX adapters in default local ingest adapters', () => {
|
||||
it('includes upload-capable ktx adapters in default local ingest adapters', () => {
|
||||
expect(createDefaultLocalIngestAdapters(project).map((adapter) => adapter.source)).toEqual(
|
||||
expect.arrayContaining(['dbt', 'metricflow', 'notion']),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ describe('memory-flow acceptance scenarios', () => {
|
|||
it('renders a completed replay with a clear saved-memory completion line', () => {
|
||||
const output = renderScenario(successfulReplayScenario());
|
||||
|
||||
expect(output).toContain('KTX memory flow warehouse/metricflow done');
|
||||
expect(output).toContain('ktx memory flow warehouse/metricflow done');
|
||||
expect(output).toContain('Saved 3 memories from 4 raw files: 2 wiki pages, 1 SL updates.');
|
||||
expect(output).toContain('Commit: abc12345 Run: run-success Report: ingest-report.json');
|
||||
});
|
||||
|
|
@ -48,7 +48,7 @@ describe('memory-flow acceptance scenarios', () => {
|
|||
it('renders no ANSI color codes in the text fallback for terminals without color support', () => {
|
||||
const output = renderScenario(successfulReplayScenario(), 80);
|
||||
|
||||
expect(output).toContain('KTX memory flow warehouse/metricflow done');
|
||||
expect(output).toContain('ktx memory flow warehouse/metricflow done');
|
||||
expect(output).not.toMatch(/\u001b\[[0-9;]*m/);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import type { MemoryFlowInteractionState, MemoryFlowViewModel } from '../../../.
|
|||
|
||||
function view(): MemoryFlowViewModel {
|
||||
return {
|
||||
title: 'KTX memory flow warehouse/metricflow running',
|
||||
title: 'ktx memory flow warehouse/metricflow running',
|
||||
subtitle: 'Run run-1 Sync sync-1',
|
||||
status: 'running',
|
||||
activeLine: 'active: WorkUnit orders step 2/4',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { MemoryFlowViewModel } from '../../../../src/context/ingest/memory-
|
|||
|
||||
function view(): MemoryFlowViewModel {
|
||||
return {
|
||||
title: 'KTX memory flow warehouse/metricflow done',
|
||||
title: 'ktx memory flow warehouse/metricflow done',
|
||||
subtitle: 'Run run-1 Sync sync-1',
|
||||
status: 'done',
|
||||
activeLine: 'active: complete',
|
||||
|
|
@ -128,7 +128,7 @@ describe('renderMemoryFlowInteractive', () => {
|
|||
|
||||
const output = renderMemoryFlowInteractive(view(), state, { terminalWidth: 140 });
|
||||
|
||||
expect(output).toContain('KTX memory flow warehouse/metricflow done');
|
||||
expect(output).toContain('ktx memory flow warehouse/metricflow done');
|
||||
expect(output).toContain('OK SOURCE -> OK CHUNKS -> !! WORKUNITS -> OK ACTIONS -> !! GATES -> OK SAVED');
|
||||
expect(output).toContain('[WORKUNITS]');
|
||||
expect(output).toContain('> orders');
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { renderMemoryFlowReplay } from '../../../../src/context/ingest/memory-fl
|
|||
|
||||
function view(): MemoryFlowViewModel {
|
||||
return {
|
||||
title: 'KTX memory flow warehouse/metricflow done',
|
||||
title: 'ktx memory flow warehouse/metricflow done',
|
||||
subtitle: 'Run run-1 Sync sync-1',
|
||||
status: 'done',
|
||||
activeLine: 'active: complete',
|
||||
|
|
@ -79,7 +79,7 @@ describe('renderMemoryFlowReplay', () => {
|
|||
'OK SOURCE -> OK CHUNKS -> !! WORKUNITS -> OK ACTIONS -> !! GATES -> OK SAVED',
|
||||
);
|
||||
expect(renderMemoryFlowReplay(view(), { terminalWidth: 140 })).toMatchInlineSnapshot(`
|
||||
"KTX memory flow warehouse/metricflow done
|
||||
"ktx memory flow warehouse/metricflow done
|
||||
active: complete
|
||||
Run run-1 Sync sync-1
|
||||
OK SOURCE -> OK CHUNKS -> !! WORKUNITS -> OK ACTIONS -> !! GATES -> OK SAVED
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ describe('buildMemoryFlowViewModel', () => {
|
|||
it('builds six readable columns from replay events', () => {
|
||||
const view = buildMemoryFlowViewModel(replayInput());
|
||||
|
||||
expect(view.title).toBe('KTX memory flow warehouse/metricflow done');
|
||||
expect(view.title).toBe('ktx memory flow warehouse/metricflow done');
|
||||
expect(view.activeLine).toBe('active: complete');
|
||||
expect(view.columns.map((column) => column.id)).toEqual([
|
||||
'source',
|
||||
|
|
@ -179,7 +179,7 @@ describe('buildMemoryFlowViewModel', () => {
|
|||
details: { actions: [], provenance: [], transcripts: [] },
|
||||
});
|
||||
|
||||
expect(view.title).toBe('KTX memory flow Warehouse + dbt + BI + Docs done');
|
||||
expect(view.title).toBe('ktx memory flow Warehouse + dbt + BI + Docs done');
|
||||
expect(view.columns.find((column) => column.id === 'source')?.counters[0]).toBe('Warehouse, dbt, BI, Docs');
|
||||
expect(view.completionLine).toContain('Saved 16 memories from 29 raw files');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function viewWithStatuses(statuses: Array<'waiting' | 'active' | 'complete' | 'w
|
|||
const ids = ['source', 'chunks', 'workUnits', 'actions', 'gates', 'saved'] as const;
|
||||
|
||||
return {
|
||||
title: 'KTX memory flow warehouse/metricflow running',
|
||||
title: 'ktx memory flow warehouse/metricflow running',
|
||||
subtitle: 'Run run-1 Sync sync-1',
|
||||
status: 'running',
|
||||
activeLine: 'active: WorkUnit orders',
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ describe('PageTriageService', () => {
|
|||
'Reusable outbound sequence:',
|
||||
'',
|
||||
'- Ask about current customer success expansion workflow.',
|
||||
'- Position KTX as AI search visibility for CS teams.',
|
||||
'- Position ktx as AI search visibility for CS teams.',
|
||||
'- Close with a discovery call request.',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
|
|
@ -233,7 +233,7 @@ describe('PageTriageService', () => {
|
|||
{
|
||||
candidateKey: 'cold-call-script',
|
||||
topic: 'Cold Call Script',
|
||||
assertion: 'Cold call outreach should position KTX around AI search visibility for CS teams.',
|
||||
assertion: 'Cold call outreach should position ktx around AI search visibility for CS teams.',
|
||||
rationale: 'The script gives a reusable outbound call sequence and positioning language.',
|
||||
evidenceChunkIds: ['00000000-0000-0000-0000-000000000101'],
|
||||
suggestedPageKey: 'cold-call-script',
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ describe('repo-fetch', () => {
|
|||
|
||||
const cacheGit = createSimpleGit(cacheDir);
|
||||
await cacheGit.addConfig('user.email', 'test@ktx.local');
|
||||
await cacheGit.addConfig('user.name', 'KTX Test');
|
||||
await cacheGit.addConfig('user.name', 'ktx Test');
|
||||
await writeFile(join(cacheDir, 'local-only.txt'), 'local commit\n', 'utf-8');
|
||||
await cacheGit.add('.');
|
||||
await cacheGit.commit('local-only divergent commit');
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ describe('ClaudeCodeKtxLlmRuntime', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('registers only exact KTX MCP tool ids and denies non-KTX tools', async () => {
|
||||
it('registers only exact ktx MCP tool ids and denies non-ktx tools', async () => {
|
||||
const query = vi.fn((_input: any) =>
|
||||
stream([
|
||||
initMessage({ tools: ['mcp__ktx__load_skill'], mcp_servers: [{ name: 'ktx', status: 'connected' }] }),
|
||||
|
|
@ -487,7 +487,7 @@ describe('ClaudeCodeKtxLlmRuntime', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('allows host-discovered context during agent loops while requiring exact KTX MCP tools and servers', async () => {
|
||||
it('allows host-discovered context during agent loops while requiring exact ktx MCP tools and servers', async () => {
|
||||
const query = vi.fn((_input: any) =>
|
||||
stream([
|
||||
initMessage({
|
||||
|
|
@ -553,7 +553,7 @@ describe('ClaudeCodeKtxLlmRuntime', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('still rejects unexpected tools, missing KTX tools, plugins, and non-KTX MCP servers from init messages', async () => {
|
||||
it('still rejects unexpected tools, missing ktx tools, plugins, and non-ktx MCP servers from init messages', async () => {
|
||||
const query = vi.fn((_input: any) =>
|
||||
stream([
|
||||
initMessage({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { KtxIngestEmbeddingPortAdapter, KtxScanEmbeddingPortAdapter } from '../../../src/context/llm/embedding-port.js';
|
||||
|
||||
describe('KTX embedding port adapters', () => {
|
||||
describe('ktx embedding port adapters', () => {
|
||||
it('adapts LLM modules embeddings to ingest embedding port shape', async () => {
|
||||
const provider = {
|
||||
dimensions: 3,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
resolveLocalKtxLlmConfig,
|
||||
} from '../../../src/context/llm/local-config.js';
|
||||
|
||||
describe('local KTX LLM config', () => {
|
||||
describe('local ktx LLM config', () => {
|
||||
it('resolves env and file references into a KtxLlmConfig', () => {
|
||||
const config: KtxProjectLlmConfig = {
|
||||
provider: {
|
||||
|
|
@ -190,7 +190,7 @@ describe('local KTX LLM config', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('local KTX embedding config', () => {
|
||||
describe('local ktx embedding config', () => {
|
||||
it('resolves sentence-transformers config', () => {
|
||||
const config: KtxProjectEmbeddingConfig = {
|
||||
backend: 'sentence-transformers',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createLocalKtxLlmProviderFromConfig, createLocalKtxLlmRuntimeFromConfig } from '../../../src/context/llm/local-config.js';
|
||||
|
||||
describe('local KTX LLM runtime config', () => {
|
||||
describe('local ktx LLM runtime config', () => {
|
||||
it('creates a Claude Code runtime for claude-code backend without creating an AI SDK provider', () => {
|
||||
const runtime = createLocalKtxLlmRuntimeFromConfig(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
"name": "connection_list",
|
||||
"title": "Connection List",
|
||||
"description": "List configured read-only data connections available to this KTX project. Use this before connection-scoped tools when the project may have multiple warehouses.",
|
||||
"description": "List configured read-only data connections available to this ktx project. Use this before connection-scoped tools when the project may have multiple warehouses.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
{
|
||||
"name": "wiki_search",
|
||||
"title": "Wiki Search",
|
||||
"description": "Search KTX wiki pages for reusable business context. Example: wiki_search({ query: \"revenue recognition\", limit: 5 }).",
|
||||
"description": "Search ktx wiki pages for reusable business context. Example: wiki_search({ query: \"revenue recognition\", limit: 5 }).",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -181,7 +181,7 @@
|
|||
{
|
||||
"name": "wiki_read",
|
||||
"title": "Wiki Read",
|
||||
"description": "Read a KTX wiki page by key returned from wiki_search. Example: wiki_read({ key: \"global/revenue\" }).",
|
||||
"description": "Read a ktx wiki page by key returned from wiki_search. Example: wiki_read({ key: \"global/revenue\" }).",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1227,7 +1227,7 @@
|
|||
{
|
||||
"name": "discover_data",
|
||||
"title": "Discover Data",
|
||||
"description": "Search across KTX wiki pages, semantic-layer sources, measures, dimensions, raw tables, and columns. Example: discover_data({ query: \"monthly orders by customer\", connectionId: \"warehouse\", kinds: [\"sl_source\", \"table\"] }).",
|
||||
"description": "Search across ktx wiki pages, semantic-layer sources, measures, dimensions, raw tables, and columns. Example: discover_data({ query: \"monthly orders by customer\", connectionId: \"warehouse\", kinds: [\"sl_source\", \"table\"] }).",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1398,7 +1398,7 @@
|
|||
{
|
||||
"name": "sql_execution",
|
||||
"title": "SQL Execution",
|
||||
"description": "Execute one parser-validated read-only SQL query against a configured KTX connection. Example: sql_execution({ connectionId: \"warehouse\", sql: \"select count(*) from public.orders\", maxRows: 100 }).",
|
||||
"description": "Execute one parser-validated read-only SQL query against a configured ktx connection. Example: sql_execution({ connectionId: \"warehouse\", sql: \"select count(*) from public.orders\", maxRows: 100 }).",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -1472,7 +1472,7 @@
|
|||
{
|
||||
"name": "memory_ingest",
|
||||
"title": "Memory Ingest",
|
||||
"description": "Ingest free-form markdown knowledge into durable KTX memory. Use this for business rules, metric definitions, schema gotchas, recurring findings, or explicit user requests to remember something. Example: memory_ingest({ connectionId: \"warehouse\", content: \"ARR is reported in cents in this warehouse.\" }).",
|
||||
"description": "Ingest free-form markdown knowledge into durable ktx memory. Use this for business rules, metric definitions, schema gotchas, recurring findings, or explicit user requests to remember something. Example: memory_ingest({ connectionId: \"warehouse\", content: \"ARR is reported in cents in this warehouse.\" }).",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
|||
|
|
@ -987,7 +987,7 @@ describe('createKtxMcpServer', () => {
|
|||
expect(ingest.ingest).toHaveBeenCalledWith({
|
||||
userId: 'mcp-user',
|
||||
chatId: expect.stringMatching(/^mcp-/),
|
||||
userMessage: 'Ingest external knowledge into KTX memory.',
|
||||
userMessage: 'Ingest external knowledge into ktx memory.',
|
||||
assistantMessage: content,
|
||||
connectionId: '00000000-0000-4000-8000-000000000001',
|
||||
sourceType: 'external_ingest',
|
||||
|
|
@ -996,7 +996,7 @@ describe('createKtxMcpServer', () => {
|
|||
const cliEquivalentInput: MemoryAgentInput = {
|
||||
userId: 'mcp-user',
|
||||
chatId: 'cli-text-ingest-test-1',
|
||||
userMessage: 'Ingest external text artifact "orders lookml" into KTX memory.',
|
||||
userMessage: 'Ingest external text artifact "orders lookml" into ktx memory.',
|
||||
assistantMessage: content,
|
||||
connectionId: '00000000-0000-4000-8000-000000000001',
|
||||
sourceType: 'external_ingest',
|
||||
|
|
@ -1101,7 +1101,7 @@ describe('createKtxMcpServer', () => {
|
|||
expect(ingestSpy).toHaveBeenCalledWith({
|
||||
userId: 'local',
|
||||
chatId: expect.stringMatching(/^mcp-/),
|
||||
userMessage: 'Ingest external knowledge into KTX memory.',
|
||||
userMessage: 'Ingest external knowledge into ktx memory.',
|
||||
assistantMessage: 'Revenue means paid order value.',
|
||||
connectionId: 'warehouse',
|
||||
sourceType: 'external_ingest',
|
||||
|
|
@ -1127,7 +1127,7 @@ describe('createKtxMcpServer', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('registers KTX context MCP tools when context ports are supplied', async () => {
|
||||
it('registers ktx context MCP tools when context ports are supplied', async () => {
|
||||
const fake = makeFakeServer();
|
||||
const contextTools: KtxMcpContextPorts = {
|
||||
connections: {
|
||||
|
|
@ -1298,7 +1298,7 @@ describe('createKtxMcpServer', () => {
|
|||
expect(jsonToolResult({ ok: true }).structuredContent).toEqual({ ok: true });
|
||||
|
||||
if (false) {
|
||||
// @ts-expect-error bare arrays are not valid MCP structuredContent objects in KTX
|
||||
// @ts-expect-error bare arrays are not valid MCP structuredContent objects in ktx
|
||||
jsonToolResult([]);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ const expectedAdapterSkillHeadings: Record<string, string> = {
|
|||
historic_sql_table_digest: '# Historic SQL Table Digest',
|
||||
live_database_ingest: '# Live Database Ingest',
|
||||
looker_ingest: '# Looker Runtime Ingest',
|
||||
lookml_ingest: '# LookML to KTX Semantic Layer',
|
||||
metabase_ingest: '# Metabase to KTX Semantic Layer',
|
||||
metricflow_ingest: '# MetricFlow to KTX Semantic Layer',
|
||||
lookml_ingest: '# LookML to ktx Semantic Layer',
|
||||
metabase_ingest: '# Metabase to ktx Semantic Layer',
|
||||
metricflow_ingest: '# MetricFlow to ktx Semantic Layer',
|
||||
};
|
||||
const verificationWriterSkills = [
|
||||
'notion_synthesize',
|
||||
|
|
@ -125,7 +125,7 @@ describe('memory runtime assets', () => {
|
|||
it('ships Metabase guidance that avoids invalid joins for SQL-only card outputs', async () => {
|
||||
const body = await readFile(join(skillsDir, 'metabase_ingest', 'SKILL.md'), 'utf-8');
|
||||
|
||||
expect(body).toContain('Do not declare a KTX join just because the card SQL joins that table internally');
|
||||
expect(body).toContain('Do not declare a ktx join just because the card SQL joins that table internally');
|
||||
expect(body).toContain('only when the card output exposes a local key that matches the target source grain');
|
||||
expect(body).toContain('If `sl_discover` resolves the table, it is not outside the manifest');
|
||||
expect(body).toContain('reason: "parse_error"');
|
||||
|
|
@ -167,7 +167,7 @@ describe('memory runtime assets', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('ships only the KTX connectionId sql_execution call shape in writer guidance', async () => {
|
||||
it('ships only the ktx connectionId sql_execution call shape in writer guidance', async () => {
|
||||
const shared = await readFile(join(skillsDir, '_shared', 'identifier-verification.md'), 'utf-8');
|
||||
const bodies = [{ name: '_shared/identifier-verification.md', body: shared }];
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
|
||||
const removedAutoCommitKey = ['auto', 'commit'].join('_');
|
||||
|
||||
describe('KTX project config', () => {
|
||||
describe('ktx project config', () => {
|
||||
it.each(['status', 'replay', 'run', 'watch'])('accepts former ingest subcommand name "%s" as a connection id', (connectionId) => {
|
||||
expect(
|
||||
parseKtxProjectConfig(`
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { join } from 'node:path';
|
|||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { initKtxProject, loadKtxProject } from '../../../src/context/project/project.js';
|
||||
|
||||
describe('KTX local project runtime', () => {
|
||||
describe('ktx local project runtime', () => {
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
setKtxSetupDatabaseConnectionIds,
|
||||
} from '../../../src/context/project/setup-config.js';
|
||||
|
||||
describe('KTX setup config helpers', () => {
|
||||
describe('ktx setup config helpers', () => {
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from '../../../src/context/scan/credentials.js';
|
||||
import type { KtxCredentialEnvelope, KtxScanReport, KtxScanWarning } from '../../../src/context/scan/types.js';
|
||||
|
||||
describe('KTX scan credential redaction', () => {
|
||||
describe('ktx scan credential redaction', () => {
|
||||
it('keeps credential references inspectable', () => {
|
||||
const envReference: KtxCredentialEnvelope = { kind: 'env', name: 'DATABASE_URL' };
|
||||
const fileReference: KtxCredentialEnvelope = { kind: 'file', path: '~/.config/ktx/warehouse' };
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
|
||||
const defaultPatterns = defaultKtxDataDictionarySettings.excludePatterns;
|
||||
|
||||
describe('KTX scan data dictionary policy', () => {
|
||||
describe('ktx scan data dictionary policy', () => {
|
||||
it('includes text-like and boolean categorical types', () => {
|
||||
expect(isKtxDataDictionaryCandidate('varchar(50)', 'status', defaultPatterns)).toBe(true);
|
||||
expect(isKtxDataDictionaryCandidate('VARCHAR', 'category', defaultPatterns)).toBe(true);
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ function createConnector(): KtxScanConnector {
|
|||
};
|
||||
}
|
||||
|
||||
describe('KTX description prompt builders', () => {
|
||||
describe('ktx description prompt builders', () => {
|
||||
it('builds column prompts with sample values, source descriptions, and nested BigQuery guidance', () => {
|
||||
const { system, user } = buildKtxColumnDescriptionPrompt({
|
||||
columnName: 'payload',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { buildKtxColumnEmbeddingText } from '../../../src/context/scan/embedding-text.js';
|
||||
|
||||
describe('KTX scan embedding text', () => {
|
||||
describe('ktx scan embedding text', () => {
|
||||
it('builds column embedding text with table, description, FK, and sample-value context', () => {
|
||||
expect(
|
||||
buildKtxColumnEmbeddingText({
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
skippedKtxScanEnrichmentSummary,
|
||||
} from '../../../src/context/scan/enrichment-summary.js';
|
||||
|
||||
describe('KTX scan enrichment summaries', () => {
|
||||
describe('ktx scan enrichment summaries', () => {
|
||||
it('keeps structural scans skipped when no enrichment was requested', () => {
|
||||
expect(failedKtxScanEnrichmentSummary('structural', false)).toEqual(skippedKtxScanEnrichmentSummary);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import type {
|
|||
KtxStructuralSyncPlan,
|
||||
} from '../../../src/context/scan/enrichment-types.js';
|
||||
|
||||
describe('KTX scan enrichment contracts', () => {
|
||||
describe('ktx scan enrichment contracts', () => {
|
||||
it('models an enriched schema with reusable table, column, and relationship metadata', () => {
|
||||
const schema: KtxEnrichedSchema = {
|
||||
connectionId: 'warehouse',
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ describe('local scan enrichment', () => {
|
|||
expect(result.summary.statisticalValidation).toBe('skipped');
|
||||
expect(result.warnings).toContainEqual({
|
||||
code: 'relationship_validation_failed',
|
||||
message: 'KTX scan connector advertises readOnlySql but does not expose executeReadOnly',
|
||||
message: 'ktx scan connector advertises readOnlySql but does not expose executeReadOnly',
|
||||
recoverable: true,
|
||||
metadata: { capability: 'readOnlySql' },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1644,7 +1644,7 @@ describe('local scan', () => {
|
|||
expect(result.report.warnings).toEqual([
|
||||
{
|
||||
code: 'enrichment_failed',
|
||||
message: 'KTX scan enrichment failed after structural scan completed: embedding service timed out',
|
||||
message: 'ktx scan enrichment failed after structural scan completed: embedding service timed out',
|
||||
recoverable: true,
|
||||
metadata: {
|
||||
mode: 'enriched',
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ describe('relationship benchmark report', () => {
|
|||
}),
|
||||
);
|
||||
|
||||
expect(markdown).toContain('# KTX Relationship Discovery Benchmark Evidence');
|
||||
expect(markdown).toContain('# ktx Relationship Discovery Benchmark Evidence');
|
||||
expect(markdown).toContain(
|
||||
'| demo_b2b_no_declared_constraints | smoke | declared_pks_and_declared_fks_removed | run | no | 0.500 | 0.000 | 0.000 | 0 |',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ describe('relationship diagnostics artifacts', () => {
|
|||
warnings: [
|
||||
{
|
||||
code: 'connector_capability_missing',
|
||||
message: 'KTX scan connector cannot run standalone statistical relationship validation',
|
||||
message: 'ktx scan connector cannot run standalone statistical relationship validation',
|
||||
recoverable: true,
|
||||
metadata: { capability: 'readOnlySql' },
|
||||
},
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ describe('production relationship discovery', () => {
|
|||
});
|
||||
expect(result.warnings).toContainEqual({
|
||||
code: 'connector_capability_missing',
|
||||
message: 'KTX scan connector cannot run read-only SQL relationship validation',
|
||||
message: 'ktx scan connector cannot run read-only SQL relationship validation',
|
||||
recoverable: true,
|
||||
metadata: { capability: 'readOnlySql' },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -146,12 +146,12 @@ describe('relationship LLM proposals', () => {
|
|||
expect(runtime.generateObject).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
role: 'candidateExtraction',
|
||||
system: expect.stringContaining('You are helping KTX review possible SQL relationships'),
|
||||
system: expect.stringContaining('You are helping ktx review possible SQL relationships'),
|
||||
prompt: expect.stringContaining('"tables"'),
|
||||
}),
|
||||
);
|
||||
const call = vi.mocked(runtime.generateObject).mock.calls[0]?.[0];
|
||||
expect(call?.prompt).not.toContain('You are helping KTX review possible SQL relationships');
|
||||
expect(call?.prompt).not.toContain('You are helping ktx review possible SQL relationships');
|
||||
});
|
||||
|
||||
it('skips when no runtime is configured', async () => {
|
||||
|
|
@ -207,7 +207,7 @@ describe('relationship LLM proposals', () => {
|
|||
expect(failed).toMatchObject({ candidates: [], llmCalls: 1, summary: 'failed' });
|
||||
expect(failed.warnings[0]).toMatchObject({
|
||||
code: 'relationship_llm_proposal_failed',
|
||||
message: 'KTX relationship LLM proposal failed: model unavailable',
|
||||
message: 'ktx relationship LLM proposal failed: model unavailable',
|
||||
recoverable: true,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { inferKtxDimensionType, ktxColumnTypeMappingFromNative, normalizeKtxNativeType } from '../../../src/context/scan/type-normalization.js';
|
||||
|
||||
describe('KTX scan type normalization', () => {
|
||||
describe('ktx scan type normalization', () => {
|
||||
it('normalizes native database type strings', () => {
|
||||
expect(normalizeKtxNativeType(' NUMERIC(12, 2) ')).toBe('numeric');
|
||||
expect(normalizeKtxNativeType('TIMESTAMP WITH TIME ZONE')).toBe('timestamp with time zone');
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
type KtxSchemaSnapshot,
|
||||
} from '../../../src/context/scan/types.js';
|
||||
|
||||
describe('KTX scan contract types', () => {
|
||||
describe('ktx scan contract types', () => {
|
||||
it('defaults to structural-only connector capabilities', () => {
|
||||
expect(createKtxConnectorCapabilities()).toEqual({
|
||||
structuralIntrospection: true,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const baseTable: SemanticLayerSource = {
|
|||
};
|
||||
|
||||
describe('listConnectionIdsWithNames', () => {
|
||||
it('discovers local KTX connection ids from semantic-layer directories', async () => {
|
||||
it('discovers local ktx connection ids from semantic-layer directories', async () => {
|
||||
const configService = {
|
||||
listFiles: vi.fn().mockResolvedValue({
|
||||
files: [
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export async function makeLocalGitRepo(fixtureDir: string, destRoot: string): Pr
|
|||
await git.init();
|
||||
await git.raw(['checkout', '-B', 'main']);
|
||||
await git.addConfig('user.email', 'test@ktx.local');
|
||||
await git.addConfig('user.name', 'KTX Test');
|
||||
await git.addConfig('user.name', 'ktx Test');
|
||||
await git.add('.');
|
||||
await git.commit('initial');
|
||||
const commit = async (message: string): Promise<string> => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue