Initial open-source release

This commit is contained in:
Andrey Avtomonov 2026-05-10 23:12:26 +02:00
commit 1a42152e6f
1199 changed files with 257054 additions and 0 deletions

View file

@ -0,0 +1,60 @@
#!/usr/bin/env node
import { createHash } from 'node:crypto';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, '..');
const fixturesRoot = path.join(repoRoot, 'packages', 'context', 'test', 'fixtures', 'relationship-benchmarks');
const manifestPath = path.join(scriptDir, 'public-benchmark-manifest.json');
export async function acquirePublicBenchmarkFixtures(options = {}) {
const fetchImpl = options.fetch ?? fetch;
const writeFile = options.writeFile ?? writeFileSync;
const readFile = options.readFile ?? readFileSync;
const fileExists = options.fileExists ?? existsSync;
const ensureDir = options.ensureDir ?? ((dir) => mkdirSync(dir, { recursive: true }));
const manifestPathOverride = options.manifestPath ?? manifestPath;
const fixturesRootOverride = options.fixturesRoot ?? fixturesRoot;
const log = options.log ?? console.log;
const manifest = JSON.parse(readFile(manifestPathOverride, 'utf8'));
const results = [];
for (const fixture of manifest.fixtures) {
const fixtureDir = path.join(fixturesRootOverride, fixture.id);
const dest = path.join(fixtureDir, 'data.sqlite');
ensureDir(fixtureDir);
if (fileExists(dest)) {
const existingHash = createHash('sha256').update(readFile(dest)).digest('hex');
if (fixture.sha256 && existingHash === fixture.sha256) {
log(`[skip] ${fixture.id}: hash matches`);
results.push({ id: fixture.id, action: 'skip', sha256: existingHash });
continue;
}
log(`[refresh] ${fixture.id}: hash mismatch (${existingHash}), re-downloading from ${fixture.url}`);
} else {
log(`[download] ${fixture.id} from ${fixture.url}`);
}
const res = await fetchImpl(fixture.url);
if (!res.ok) {
throw new Error(`Failed to download ${fixture.id} from ${fixture.url}: HTTP ${res.status}`);
}
const buf = Buffer.from(await res.arrayBuffer());
const hash = createHash('sha256').update(buf).digest('hex');
if (fixture.sha256 && hash !== fixture.sha256) {
throw new Error(`Hash mismatch for ${fixture.id}: expected ${fixture.sha256}, got ${hash}`);
}
writeFile(dest, buf);
log(`[done] ${fixture.id}: sha256=${hash} bytes=${buf.length}`);
results.push({ id: fixture.id, action: 'downloaded', sha256: hash, bytes: buf.length });
}
return results;
}
if (import.meta.url === `file://${process.argv[1]}`) {
acquirePublicBenchmarkFixtures().catch((err) => {
console.error(err);
process.exit(1);
});
}

View file

@ -0,0 +1,168 @@
import assert from 'node:assert/strict';
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { describe, it } from 'node:test';
import { acquirePublicBenchmarkFixtures } from './acquire-public-benchmark-fixtures.mjs';
function tempRoot() {
return mkdtempSync(path.join(tmpdir(), 'klo-acquire-'));
}
function writeManifest(dir, fixtures) {
const p = path.join(dir, 'manifest.json');
writeFileSync(p, JSON.stringify({ fixtures }), 'utf8');
return p;
}
describe('acquirePublicBenchmarkFixtures', () => {
it('downloads, hashes, and writes data.sqlite for each manifest entry', async () => {
const root = tempRoot();
try {
const fixturesRoot = path.join(root, 'fixtures');
const manifestPath = writeManifest(root, [
{ id: 'foo_fixture', url: 'https://example.invalid/foo', sha256: '' },
]);
const calls = [];
const result = await acquirePublicBenchmarkFixtures({
manifestPath,
fixturesRoot,
fetch: async (url) => {
calls.push(url);
return {
ok: true,
status: 200,
async arrayBuffer() {
return Buffer.from('hello-sqlite');
},
};
},
log: () => {},
});
assert.equal(calls.length, 1);
assert.equal(calls[0], 'https://example.invalid/foo');
assert.equal(result.length, 1);
assert.equal(result[0].action, 'downloaded');
const dest = path.join(fixturesRoot, 'foo_fixture', 'data.sqlite');
assert.ok(existsSync(dest));
assert.equal(readFileSync(dest, 'utf8'), 'hello-sqlite');
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it('skips when existing file matches the manifest sha256', async () => {
const root = tempRoot();
try {
const fixturesRoot = path.join(root, 'fixtures');
const fixtureDir = path.join(fixturesRoot, 'foo_fixture');
const dest = path.join(fixtureDir, 'data.sqlite');
const { mkdirSync } = await import('node:fs');
mkdirSync(fixtureDir, { recursive: true });
writeFileSync(dest, Buffer.from('hello-sqlite'));
const expectedHash = '52a3e2d435cdf97a44eca3dd4882d008b9ef73b63bc75476d320fdd665c812c0'; // pragma: allowlist secret
const manifestPath = writeManifest(root, [
{ id: 'foo_fixture', url: 'https://example.invalid/foo', sha256: expectedHash },
]);
let fetchCalls = 0;
const result = await acquirePublicBenchmarkFixtures({
manifestPath,
fixturesRoot,
fetch: async () => {
fetchCalls += 1;
throw new Error('should not fetch');
},
log: () => {},
});
assert.equal(result[0].action, 'skip');
assert.equal(fetchCalls, 0);
assert.equal(readFileSync(dest, 'utf8'), 'hello-sqlite');
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it('throws when the downloaded payload sha256 does not match the manifest', async () => {
const root = tempRoot();
try {
const fixturesRoot = path.join(root, 'fixtures');
const manifestPath = writeManifest(root, [
{
id: 'foo_fixture',
url: 'https://example.invalid/foo',
sha256: '0000000000000000000000000000000000000000000000000000000000000000',
},
]);
await assert.rejects(
acquirePublicBenchmarkFixtures({
manifestPath,
fixturesRoot,
fetch: async () => ({
ok: true,
status: 200,
async arrayBuffer() {
return Buffer.from('different-payload');
},
}),
log: () => {},
}),
/Hash mismatch/,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it('surfaces non-OK HTTP statuses with the fixture id', async () => {
const root = tempRoot();
try {
const fixturesRoot = path.join(root, 'fixtures');
const manifestPath = writeManifest(root, [
{ id: 'foo_fixture', url: 'https://example.invalid/foo', sha256: '' },
]);
await assert.rejects(
acquirePublicBenchmarkFixtures({
manifestPath,
fixturesRoot,
fetch: async () => ({
ok: false,
status: 404,
async arrayBuffer() {
return Buffer.alloc(0);
},
}),
log: () => {},
}),
/foo_fixture .* HTTP 404/,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it('pins every checked-in public benchmark fixture download in the manifest', () => {
const manifestPath = new URL('./public-benchmark-manifest.json', import.meta.url);
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
const fixtureIds = manifest.fixtures.map((fixture) => fixture.id).sort();
assert.deepEqual(fixtureIds, [
'adventureworkslt_with_declared_metadata',
'chinook_with_declared_metadata',
'northwind_with_declared_metadata',
'sakila_with_declared_metadata',
]);
const adventureWorks = manifest.fixtures.find(
(fixture) => fixture.id === 'adventureworkslt_with_declared_metadata',
);
assert.ok(adventureWorks);
assert.equal(adventureWorks.displayName, 'AdventureWorksLT (SQLite, declared metadata)');
assert.equal(
adventureWorks.url,
'https://github.com/nuitsjp/AdventureWorks-for-SQLite/releases/download/Release-1_0_0/AdventureWorksLT.db',
);
assert.equal(adventureWorks.sha256, 'f1a87a31f4efb5654f57a3b1ca47fac338972ceb7553673d66ea0bd9d55a7008'); // pragma: allowlist secret
assert.equal(adventureWorks.license, 'MIT');
assert.equal(adventureWorks.source, 'https://github.com/nuitsjp/AdventureWorks-for-SQLite');
});
});

View file

@ -0,0 +1,13 @@
{
"id": "adventureworks_oltp_with_declared_metadata",
"displayName": "AdventureWorks OLTP (SQL Server 2022, declared metadata)",
"installScriptUrl": "https://github.com/microsoft/sql-server-samples/releases/download/adventureworks/AdventureWorks-oltp-install-script.zip",
"installScriptSha256": "58962e94ea386ef7cd3d8a08211bfd42a79d9b81bdd68fd4b6b0051de6c5bd42", "_allowlist": "// pragma: allowlist secret",
"source": "https://github.com/microsoft/sql-server-samples/tree/master/samples/databases/adventure-works",
"license": "MIT",
"expectedTables": 71,
"expectedPrimaryKeys": 71,
"expectedForeignKeys": 90,
"expectedCsvFiles": 69,
"notes": "Full OLTP AdventureWorks corpus. Do not replace with AdventureWorksLT; the LT SQLite source is already covered by adventureworkslt_with_declared_metadata."
}

View file

@ -0,0 +1,25 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { describe, it } from 'node:test';
describe('AdventureWorks OLTP benchmark source metadata', () => {
it('pins the full OLTP source instead of the lightweight LT source', () => {
const source = JSON.parse(readFileSync(new URL('./adventureworks-oltp-source.json', import.meta.url), 'utf8'));
assert.equal(source.id, 'adventureworks_oltp_with_declared_metadata');
assert.equal(source.displayName, 'AdventureWorks OLTP (SQL Server 2022, declared metadata)');
assert.equal(
source.installScriptUrl,
'https://github.com/microsoft/sql-server-samples/releases/download/adventureworks/AdventureWorks-oltp-install-script.zip',
);
assert.equal(source.installScriptSha256, '58962e94ea386ef7cd3d8a08211bfd42a79d9b81bdd68fd4b6b0051de6c5bd42'); // pragma: allowlist secret
assert.equal(source.license, 'MIT');
assert.equal(source.source, 'https://github.com/microsoft/sql-server-samples/tree/master/samples/databases/adventure-works');
assert.equal(source.expectedTables, 71);
assert.equal(source.expectedPrimaryKeys, 71);
assert.equal(source.expectedForeignKeys, 90);
assert.equal(source.expectedCsvFiles, 69);
assert.match(source.notes, /full OLTP/i);
assert.doesNotMatch(JSON.stringify(source), /AdventureWorksLT\.db|Release-1_0_0|nuitsjp/);
});
});

View file

@ -0,0 +1,66 @@
import assert from 'node:assert/strict';
import { readdir, readFile } from 'node:fs/promises';
import { describe, it } from 'node:test';
const KLO_ROOT = new URL('../', import.meta.url);
const RELATIONSHIP_RUNTIME_SOURCES = Object.freeze([
'packages/context/src/scan/relationship-benchmarks.ts',
'packages/context/src/scan/relationship-budget.ts',
'packages/context/src/scan/relationship-candidates.ts',
'packages/context/src/scan/relationship-composite-candidates.ts',
'packages/context/src/scan/relationship-graph-resolver.ts',
'packages/context/src/scan/relationship-locality.ts',
'packages/context/src/scan/relationship-name-similarity.ts',
'packages/context/src/scan/relationship-discovery.ts',
'packages/context/src/scan/relationship-profiling.ts',
'packages/context/src/scan/relationship-scoring.ts',
'packages/context/src/scan/relationship-validation.ts',
]);
async function checkedInFixtureIds() {
const fixtureRoot = new URL('packages/context/test/fixtures/relationship-benchmarks/', KLO_ROOT);
const entries = await readdir(fixtureRoot, { withFileTypes: true });
return entries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort((left, right) => left.localeCompare(right));
}
async function readRuntimeSources() {
return Promise.all(
RELATIONSHIP_RUNTIME_SOURCES.map(async (relativePath) => ({
relativePath,
source: await readFile(new URL(relativePath, KLO_ROOT), 'utf8'),
})),
);
}
describe('relationship evidence-fusion source guardrails', () => {
it('keeps runtime relationship modules free of fixture-id conditionals', async () => {
const fixtureIds = await checkedInFixtureIds();
const sources = await readRuntimeSources();
const hits = [];
for (const { relativePath, source } of sources) {
for (const fixtureId of fixtureIds) {
if (source.includes(fixtureId)) {
hits.push(`${relativePath}: ${fixtureId}`);
}
}
}
assert.deepEqual(hits, []);
});
it('keeps runtime relationship modules free of length-threshold drop-all cliffs', async () => {
const sources = await readRuntimeSources();
const dropAllPattern = /if\s*\([^)]*\.length\s*>\s*\d+[^)]*\)\s*(?:\{\s*)?return\s*\[\];/gs;
const hits = sources.flatMap(({ relativePath, source }) => {
const matches = Array.from(source.matchAll(dropAllPattern));
return matches.map((match) => `${relativePath}: ${match[0].replace(/\s+/g, ' ').trim()}`);
});
assert.deepEqual(hits, []);
});
});

View file

@ -0,0 +1,260 @@
#!/usr/bin/env node
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { expectedLinksFromSnapshot, normalizeSqliteType } from './build-benchmark-snapshot.mjs';
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, '..');
const require = createRequire(new URL('../packages/context/package.json', import.meta.url));
const Database = require('better-sqlite3');
const { stringify: yamlStringify } = require('yaml');
const fixtureId = 'adventureworks_oltp_with_declared_metadata';
const defaultFixtureDir = path.join(
repoRoot,
'packages',
'context',
'test',
'fixtures',
'relationship-benchmarks',
fixtureId,
);
function quoteSqliteIdentifier(value) {
return `"${String(value).replaceAll('"', '""')}"`;
}
function quoteSqlServerIdentifier(value) {
return `[${String(value).replaceAll(']', ']]')}]`;
}
function flattenTableName(table) {
return `${table.db}.${table.name}`;
}
function sqliteDimensionType(nativeType, columnName) {
const type = normalizeSqliteType(nativeType);
const name = columnName.toLowerCase();
if (/date|time/.test(name) || /date|time/.test(String(nativeType).toLowerCase())) {
return 'time';
}
if (type === 'integer' || type === 'real') {
return 'number';
}
return 'string';
}
function sqliteValue(value) {
if (value === undefined) {
return null;
}
if (value instanceof Date) {
return value.toISOString();
}
if (typeof value === 'boolean') {
return value ? 1 : 0;
}
if (Buffer.isBuffer(value)) {
return value;
}
if (typeof value === 'object' && value !== null) {
return JSON.stringify(value);
}
return value;
}
export function snapshotForSqliteBenchmark(sqlServerSnapshot) {
const tableNameByOriginal = new Map(
sqlServerSnapshot.tables
.filter((table) => table.kind === 'table')
.map((table) => [`${table.db}.${table.name}`, flattenTableName(table)]),
);
return {
connectionId: fixtureId,
driver: 'sqlite',
extractedAt: sqlServerSnapshot.extractedAt,
scope: { catalogs: ['main'], schemas: ['main'] },
metadata: {
...sqlServerSnapshot.metadata,
source_driver: 'sqlserver',
source_connection_id: sqlServerSnapshot.connectionId,
source_database: sqlServerSnapshot.metadata?.database ?? null,
},
tables: sqlServerSnapshot.tables
.filter((table) => table.kind === 'table')
.map((table) => ({
catalog: null,
db: 'main',
name: flattenTableName(table),
kind: 'table',
comment: table.comment ?? null,
estimatedRows: table.estimatedRows ?? 0,
columns: table.columns.map((column) => ({
name: column.name,
nativeType: column.nativeType,
normalizedType: normalizeSqliteType(column.nativeType),
dimensionType: sqliteDimensionType(column.nativeType, column.name),
nullable: column.nullable,
primaryKey: column.primaryKey,
comment: column.comment ?? null,
})),
foreignKeys: (table.foreignKeys ?? []).flatMap((fk) => {
const originalTarget = `${fk.toDb}.${fk.toTable}`;
const targetName = tableNameByOriginal.get(originalTarget);
if (!targetName) {
return [];
}
return [
{
fromColumn: fk.fromColumn,
toCatalog: null,
toDb: 'main',
toTable: targetName,
toColumn: fk.toColumn,
constraintName: fk.constraintName,
},
];
}),
})),
};
}
export function writeAdventureWorksFixtureConfig(fixtureDir) {
const fixture = {
id: fixtureId,
name: 'AdventureWorks OLTP (SQL Server 2022, declared metadata)',
tier: 'row_bearing',
thresholdEligible: true,
defaultModes: [
'metadata_present',
'declared_pks_and_declared_fks_removed',
'declared_pks_removed',
'declared_fks_removed',
'profiling_disabled',
'validation_disabled',
'llm_disabled',
'embeddings_disabled',
],
};
writeFileSync(path.join(fixtureDir, 'fixture.yaml'), yamlStringify(fixture), 'utf8');
}
export function writeAdventureWorksSnapshotAndLabels(fixtureDir, sqliteSnapshot) {
writeFileSync(path.join(fixtureDir, 'snapshot.json'), `${JSON.stringify(sqliteSnapshot, null, 2)}\n`, 'utf8');
writeFileSync(path.join(fixtureDir, 'expected-links.yaml'), yamlStringify(expectedLinksFromSnapshot(sqliteSnapshot)), 'utf8');
}
export async function copySqlServerRowsToSqlite(input) {
const { connector, sourceSnapshot, sqliteSnapshot, fixtureDir } = input;
const sqlitePath = path.join(fixtureDir, 'data.sqlite');
rmSync(sqlitePath, { force: true });
const db = new Database(sqlitePath);
try {
db.pragma('journal_mode = WAL');
db.exec('BEGIN');
for (const sourceTable of sourceSnapshot.tables.filter((table) => table.kind === 'table')) {
const sqliteTable = sqliteSnapshot.tables.find((table) => table.name === flattenTableName(sourceTable));
if (!sqliteTable) {
continue;
}
const columns = sqliteTable.columns;
const createColumns = columns
.map((column) => `${quoteSqliteIdentifier(column.name)} ${normalizeSqliteType(column.nativeType).toUpperCase()}`)
.join(', ');
db.exec(`CREATE TABLE ${quoteSqliteIdentifier(sqliteTable.name)} (${createColumns})`);
const selectSql = `SELECT * FROM ${quoteSqlServerIdentifier(sourceTable.db)}.${quoteSqlServerIdentifier(sourceTable.name)}`;
const result = await connector.executeReadOnly(
{
connectionId: sourceSnapshot.connectionId,
sql: selectSql,
maxRows: Math.max(sourceTable.estimatedRows ?? 0, 1000000),
},
{ runId: `adventureworks-oltp-copy:${sqliteTable.name}` },
);
const bindSlots = columns.map(() => '?').join(', ');
const insert = db.prepare(
`INSERT INTO ${quoteSqliteIdentifier(sqliteTable.name)} (${columns
.map((column) => quoteSqliteIdentifier(column.name))
.join(', ')}) VALUES (${bindSlots})`,
);
for (const row of result.rows) {
insert.run(row.map(sqliteValue));
}
}
db.exec('COMMIT');
} catch (error) {
db.exec('ROLLBACK');
throw error;
} finally {
db.close();
}
}
export async function buildAdventureWorksOltpFixture(input) {
const fixtureDir = input.fixtureDir ?? defaultFixtureDir;
mkdirSync(fixtureDir, { recursive: true });
const sourceSnapshot = await input.connector.introspect(
{ connectionId: input.connectionId, driver: 'sqlserver' },
{ runId: 'adventureworks-oltp-fixture:introspect' },
);
const sqliteSnapshot = snapshotForSqliteBenchmark(sourceSnapshot);
writeAdventureWorksFixtureConfig(fixtureDir);
writeAdventureWorksSnapshotAndLabels(fixtureDir, sqliteSnapshot);
await copySqlServerRowsToSqlite({ connector: input.connector, sourceSnapshot, sqliteSnapshot, fixtureDir });
return {
fixtureDir,
tableCount: sqliteSnapshot.tables.length,
expected: expectedLinksFromSnapshot(sqliteSnapshot),
};
}
async function main() {
const url = process.env.KLO_ADVENTUREWORKS_SQLSERVER_URL;
if (!url) {
throw new Error(
'Set KLO_ADVENTUREWORKS_SQLSERVER_URL to a read-only SQL Server URL for a full AdventureWorks OLTP database before running this script.',
);
}
const source = JSON.parse(readFileSync(path.join(scriptDir, 'adventureworks-oltp-source.json'), 'utf8'));
const { KloSqlServerScanConnector } = await import('../packages/connector-sqlserver/dist/index.js');
const connector = new KloSqlServerScanConnector({
connectionId: fixtureId,
connection: {
driver: 'sqlserver',
url,
schemas: ['dbo', 'HumanResources', 'Person', 'Production', 'Purchasing', 'Sales'],
readonly: true,
trustServerCertificate: true,
},
now: () => new Date('2026-05-07T00:00:00.000Z'),
});
const result = await buildAdventureWorksOltpFixture({ connector, connectionId: fixtureId });
if (result.tableCount !== source.expectedTables) {
throw new Error(`Expected ${source.expectedTables} tables, generated ${result.tableCount}`);
}
if (result.expected.expectedPks.length !== source.expectedPrimaryKeys) {
throw new Error(`Expected ${source.expectedPrimaryKeys} PK entries, generated ${result.expected.expectedPks.length}`);
}
if (result.expected.expectedLinks.length !== source.expectedForeignKeys) {
throw new Error(`Expected ${source.expectedForeignKeys} FK links, generated ${result.expected.expectedLinks.length}`);
}
console.log(
`[built] ${fixtureId}: ${result.tableCount} tables, ${result.expected.expectedPks.length} PKs, ${result.expected.expectedLinks.length} FKs`,
);
}
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}

View file

@ -0,0 +1,267 @@
#!/usr/bin/env node
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(new URL('../packages/context/package.json', import.meta.url));
const Database = require('better-sqlite3');
const { stringify: yamlStringify } = require('yaml');
const TIME_PATTERNS = /(_at$|_date$|^date_|_time$|^timestamp_)/i;
const TIME_TYPES = /(date|time|timestamp)/i;
function quoteIdentifier(value) {
return `"${String(value).replaceAll('"', '""')}"`;
}
export function normalizeSqliteType(rawType) {
const t = (rawType || '').toLowerCase().trim();
if (!t) {
return 'text';
}
if (/int/.test(t)) {
return 'integer';
}
if (/char|text|clob/.test(t)) {
return 'text';
}
if (/real|float|double|numeric|decimal/.test(t)) {
return 'real';
}
if (/blob/.test(t)) {
return 'blob';
}
if (/bool/.test(t)) {
return 'integer';
}
if (/date|time/.test(t)) {
return 'text';
}
return 'text';
}
export function dimensionTypeFor(rawType, columnName) {
const t = (rawType || '').toLowerCase();
const n = (columnName || '').toLowerCase();
if (TIME_PATTERNS.test(n) || TIME_TYPES.test(t)) {
return 'time';
}
if (/bool/.test(t)) {
return 'boolean';
}
if (/int|real|float|double|numeric|decimal/.test(t)) {
return 'number';
}
return 'string';
}
function tableNames(db) {
return db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
.all()
.map((row) => row.name);
}
function columnsFor(db, table) {
return db
.prepare(`PRAGMA table_info(${quoteIdentifier(table)})`)
.all()
.map((c) => ({
cid: c.cid,
name: c.name,
nativeType: c.type ?? '',
nullable: !c.notnull,
primaryKey: c.pk > 0,
pkOrdinal: c.pk > 0 ? c.pk : null,
}));
}
function rawForeignKeys(db, table) {
return db.prepare(`PRAGMA foreign_key_list(${quoteIdentifier(table)})`).all();
}
function rowCount(db, table) {
const row = db.prepare(`SELECT COUNT(*) AS c FROM ${quoteIdentifier(table)}`).get();
return Number(row?.c ?? 0);
}
function groupedForeignKeys(rawFks, table) {
const byId = new Map();
for (const row of rawFks) {
const list = byId.get(row.id) ?? [];
list.push(row);
byId.set(row.id, list);
}
const out = [];
for (const rows of byId.values()) {
rows.sort((a, b) => a.seq - b.seq);
out.push({
from: rows.map((r) => r.from),
toTable: rows[0].table,
to: rows.map((r) => r.to),
constraintName: `${table}_${rows.map((r) => r.from).join('_')}_fkey`,
});
}
return out;
}
function groupedSnapshotForeignKeys(table) {
const byKey = new Map();
for (const fk of table.foreignKeys ?? []) {
const key = fk.constraintName ?? `${table.name}:${fk.toTable}:${fk.toColumn}`;
const rows = byKey.get(key) ?? [];
rows.push(fk);
byKey.set(key, rows);
}
return [...byKey.values()].map((rows) => ({
fromTable: table.name,
fromColumns: rows.map((row) => row.fromColumn),
toTable: rows[0].toTable,
toColumns: rows.map((row) => row.toColumn),
relationship: 'many_to_one',
}));
}
export function expectedLinksFromSnapshot(snapshot) {
const expectedPks = [];
const expectedLinks = [];
for (const table of snapshot.tables ?? []) {
if (table.kind !== 'table') {
continue;
}
const pkColumns = (table.columns ?? []).filter((column) => column.primaryKey).map((column) => column.name);
if (pkColumns.length) {
expectedPks.push({ table: table.name, columns: pkColumns });
}
expectedLinks.push(...groupedSnapshotForeignKeys(table));
}
expectedPks.sort((left, right) => left.table.localeCompare(right.table));
expectedLinks.sort((left, right) => {
const leftKey = `${left.fromTable}.${left.fromColumns.join(',')}->${left.toTable}.${left.toColumns.join(',')}`;
const rightKey = `${right.fromTable}.${right.fromColumns.join(',')}->${right.toTable}.${right.toColumns.join(',')}`;
return leftKey.localeCompare(rightKey);
});
return { expectedPks, expectedLinks };
}
export function buildBenchmarkSnapshot(input) {
const { db, fixtureId, extractedAt } = input;
const names = tableNames(db);
const tables = [];
for (const name of names) {
const cols = columnsFor(db, name);
const grouped = groupedForeignKeys(rawForeignKeys(db, name), name);
const estimatedRows = rowCount(db, name);
const columns = cols.map((c) => ({
name: c.name,
nativeType: c.nativeType,
normalizedType: normalizeSqliteType(c.nativeType),
dimensionType: dimensionTypeFor(c.nativeType, c.name),
nullable: c.nullable,
primaryKey: c.primaryKey,
comment: null,
}));
const foreignKeys = grouped.flatMap((g) =>
g.from.map((fromColumn, index) => ({
fromColumn,
toCatalog: null,
toDb: 'main',
toTable: g.toTable,
toColumn: g.to[index],
constraintName: g.constraintName,
})),
);
tables.push({
catalog: null,
db: 'main',
name,
kind: 'table',
comment: null,
estimatedRows,
columns,
foreignKeys,
});
}
return {
snapshot: {
connectionId: fixtureId,
driver: 'sqlite',
extractedAt: extractedAt ?? '2026-05-07T00:00:00.000Z',
scope: {},
metadata: {},
tables,
},
expected: expectedLinksFromSnapshot({
connectionId: fixtureId,
driver: 'sqlite',
extractedAt: extractedAt ?? '2026-05-07T00:00:00.000Z',
scope: {},
metadata: {},
tables,
}),
};
}
export function writeFixtureFiles(input) {
const { fixtureDir, snapshot, expected } = input;
writeFileSync(path.join(fixtureDir, 'snapshot.json'), `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
writeFileSync(path.join(fixtureDir, 'expected-links.yaml'), yamlStringify(expected), 'utf8');
}
export function rebuildAllPublicSnapshots(options = {}) {
const repoRoot = options.repoRoot ?? path.resolve(scriptDir, '..');
const fixturesRoot =
options.fixturesRoot ?? path.join(repoRoot, 'packages', 'context', 'test', 'fixtures', 'relationship-benchmarks');
const manifestPath = options.manifestPath ?? path.join(scriptDir, 'public-benchmark-manifest.json');
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
for (const fixture of manifest.fixtures) {
const fixtureDir = path.join(fixturesRoot, fixture.id);
const dataPath = path.join(fixtureDir, 'data.sqlite');
if (!existsSync(dataPath)) {
console.log(`[skip] ${fixture.id}: data.sqlite missing (run relationships:acquire-public-fixtures first)`);
continue;
}
const db = new Database(dataPath, { readonly: true });
try {
const result = buildBenchmarkSnapshot({ db, fixtureId: fixture.id });
writeFixtureFiles({ fixtureDir, snapshot: result.snapshot, expected: result.expected });
console.log(
`[built] ${fixture.id}: ${result.snapshot.tables.length} tables, ${result.expected.expectedLinks.length} expected links`,
);
} finally {
db.close();
}
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
const args = process.argv.slice(2);
if (args[0] === '--rebuild-all') {
rebuildAllPublicSnapshots();
} else if (args.length === 2) {
const [dataPath, fixtureDir] = args;
const db = new Database(dataPath, { readonly: true });
try {
const fixtureId = path.basename(fixtureDir);
const result = buildBenchmarkSnapshot({ db, fixtureId });
writeFixtureFiles({ fixtureDir, snapshot: result.snapshot, expected: result.expected });
console.log(`[built] ${fixtureId}`);
} finally {
db.close();
}
} else {
console.error('Usage: build-benchmark-snapshot.mjs <data.sqlite> <fixtureDir> | --rebuild-all');
process.exit(2);
}
}

View file

@ -0,0 +1,253 @@
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { describe, it } from 'node:test';
import { buildBenchmarkSnapshot } from './build-benchmark-snapshot.mjs';
const require = createRequire(new URL('../packages/context/package.json', import.meta.url));
const Database = require('better-sqlite3');
describe('buildBenchmarkSnapshot', () => {
it('emits a KloSchemaSnapshot-shaped object plus expected-links from declared FKs', () => {
const db = new Database(':memory:');
db.exec(`
PRAGMA foreign_keys = ON;
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
account_id INTEGER NOT NULL REFERENCES accounts(id),
total REAL,
created_at TEXT
);
INSERT INTO accounts (id, name) VALUES (1, 'a'), (2, 'b');
INSERT INTO orders (id, account_id, total, created_at) VALUES
(1, 1, 10.0, '2024-01-01'), (2, 1, 20.0, '2024-01-02'), (3, 2, 30.0, '2024-01-03');
`);
const result = buildBenchmarkSnapshot({ db, fixtureId: 'fixture_x' });
db.close();
assert.equal(result.snapshot.connectionId, 'fixture_x');
assert.equal(result.snapshot.driver, 'sqlite');
assert.equal(result.snapshot.tables.length, 2);
const accounts = result.snapshot.tables.find((t) => t.name === 'accounts');
assert.ok(accounts);
assert.equal(accounts.estimatedRows, 2);
assert.deepEqual(accounts.foreignKeys, []);
const idCol = accounts.columns.find((c) => c.name === 'id');
assert.equal(idCol.primaryKey, true);
assert.equal(idCol.normalizedType, 'integer');
assert.equal(idCol.dimensionType, 'number');
const orders = result.snapshot.tables.find((t) => t.name === 'orders');
assert.equal(orders.foreignKeys.length, 1);
assert.equal(orders.foreignKeys[0].fromColumn, 'account_id');
assert.equal(orders.foreignKeys[0].toTable, 'accounts');
assert.equal(orders.foreignKeys[0].toColumn, 'id');
const createdAt = orders.columns.find((c) => c.name === 'created_at');
assert.equal(createdAt.dimensionType, 'time');
const total = orders.columns.find((c) => c.name === 'total');
assert.equal(total.dimensionType, 'number');
assert.equal(total.nullable, true);
assert.deepEqual(
result.expected.expectedPks.sort((a, b) => a.table.localeCompare(b.table)),
[
{ table: 'accounts', columns: ['id'] },
{ table: 'orders', columns: ['id'] },
],
);
assert.deepEqual(result.expected.expectedLinks, [
{
fromTable: 'orders',
fromColumns: ['account_id'],
toTable: 'accounts',
toColumns: ['id'],
relationship: 'many_to_one',
},
]);
});
it('skips internal SQLite tables (sqlite_*) and views', () => {
const db = new Database(':memory:');
db.exec(`
CREATE TABLE keep_me (id INTEGER PRIMARY KEY);
CREATE VIEW keep_me_view AS SELECT id FROM keep_me;
INSERT INTO keep_me (id) VALUES (1);
`);
const result = buildBenchmarkSnapshot({ db, fixtureId: 'fx' });
db.close();
assert.equal(result.snapshot.tables.length, 1);
assert.equal(result.snapshot.tables[0].name, 'keep_me');
});
it('groups composite foreign keys into a single ordered link', () => {
const db = new Database(':memory:');
db.exec(`
PRAGMA foreign_keys = ON;
CREATE TABLE order_lines (
order_id INTEGER NOT NULL,
line_number INTEGER NOT NULL,
sku TEXT NOT NULL,
PRIMARY KEY (order_id, line_number)
);
CREATE TABLE allocations (
id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
line_number INTEGER NOT NULL,
FOREIGN KEY (order_id, line_number) REFERENCES order_lines(order_id, line_number)
);
`);
const result = buildBenchmarkSnapshot({ db, fixtureId: 'fx' });
db.close();
const composite = result.expected.expectedLinks.find((l) => l.fromTable === 'allocations');
assert.deepEqual(composite, {
fromTable: 'allocations',
fromColumns: ['order_id', 'line_number'],
toTable: 'order_lines',
toColumns: ['order_id', 'line_number'],
relationship: 'many_to_one',
});
const compositePk = result.expected.expectedPks.find((p) => p.table === 'order_lines');
assert.deepEqual(compositePk.columns, ['order_id', 'line_number']);
});
it('derives expected PKs and grouped FKs from an existing snapshot', async () => {
const { expectedLinksFromSnapshot } = await import('./build-benchmark-snapshot.mjs');
const expected = expectedLinksFromSnapshot({
connectionId: 'fixture',
driver: 'sqlite',
extractedAt: '2026-05-07T00:00:00.000Z',
scope: {},
metadata: {},
tables: [
{
catalog: null,
db: 'main',
name: 'Sales.SalesOrderHeader',
kind: 'table',
comment: null,
estimatedRows: 3,
columns: [
{
name: 'SalesOrderID',
nativeType: 'int',
normalizedType: 'integer',
dimensionType: 'number',
nullable: false,
primaryKey: true,
comment: null,
},
{
name: 'CustomerID',
nativeType: 'int',
normalizedType: 'integer',
dimensionType: 'number',
nullable: false,
primaryKey: false,
comment: null,
},
],
foreignKeys: [
{
fromColumn: 'CustomerID',
toCatalog: null,
toDb: 'main',
toTable: 'Sales.Customer',
toColumn: 'CustomerID',
constraintName: 'FK_SalesOrderHeader_Customer_CustomerID',
},
],
},
{
catalog: null,
db: 'main',
name: 'Sales.Customer',
kind: 'table',
comment: null,
estimatedRows: 2,
columns: [
{
name: 'CustomerID',
nativeType: 'int',
normalizedType: 'integer',
dimensionType: 'number',
nullable: false,
primaryKey: true,
comment: null,
},
],
foreignKeys: [],
},
{
catalog: null,
db: 'main',
name: 'Sales.SalesOrderDetail',
kind: 'table',
comment: null,
estimatedRows: 6,
columns: [
{
name: 'SalesOrderID',
nativeType: 'int',
normalizedType: 'integer',
dimensionType: 'number',
nullable: false,
primaryKey: true,
comment: null,
},
{
name: 'SalesOrderDetailID',
nativeType: 'int',
normalizedType: 'integer',
dimensionType: 'number',
nullable: false,
primaryKey: true,
comment: null,
},
],
foreignKeys: [
{
fromColumn: 'SalesOrderID',
toCatalog: null,
toDb: 'main',
toTable: 'Sales.SalesOrderHeader',
toColumn: 'SalesOrderID',
constraintName: 'FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID',
},
],
},
],
});
assert.deepEqual(expected.expectedPks, [
{ table: 'Sales.Customer', columns: ['CustomerID'] },
{ table: 'Sales.SalesOrderDetail', columns: ['SalesOrderID', 'SalesOrderDetailID'] },
{ table: 'Sales.SalesOrderHeader', columns: ['SalesOrderID'] },
]);
assert.deepEqual(expected.expectedLinks, [
{
fromTable: 'Sales.SalesOrderDetail',
fromColumns: ['SalesOrderID'],
toTable: 'Sales.SalesOrderHeader',
toColumns: ['SalesOrderID'],
relationship: 'many_to_one',
},
{
fromTable: 'Sales.SalesOrderHeader',
fromColumns: ['CustomerID'],
toTable: 'Sales.Customer',
toColumns: ['CustomerID'],
relationship: 'many_to_one',
},
]);
});
});

View file

@ -0,0 +1,492 @@
#!/usr/bin/env node
import { mkdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { gzipSync } from 'node:zlib';
import { buildBenchmarkSnapshot, writeFixtureFiles } from './build-benchmark-snapshot.mjs';
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const kloRoot = path.resolve(scriptDir, '..');
const fixtureRoot = path.join(kloRoot, 'packages', 'context', 'test', 'fixtures', 'relationship-benchmarks');
const require = createRequire(new URL('../packages/context/package.json', import.meta.url));
const Database = require('better-sqlite3');
const { stringify: yamlStringify } = require('yaml');
function q(value) {
return `"${String(value).replaceAll('"', '""')}"`;
}
function sqlValue(value) {
if (value === null) {
return 'NULL';
}
if (typeof value === 'number') {
return String(value);
}
return `'${String(value).replaceAll("'", "''")}'`;
}
function insertSql(table, columns, rows) {
return `INSERT INTO ${q(table)} (${columns.map(q).join(', ')}) VALUES\n${rows
.map((row) => ` (${row.map(sqlValue).join(', ')})`)
.join(',\n')};`;
}
function fixtureYaml(config) {
return yamlStringify({
id: config.id,
name: config.name,
tier: config.tier,
origin: 'synthetic',
thresholdEligible: false,
...(config.validationBudget === undefined ? {} : { validationBudget: config.validationBudget }),
defaultModes: ['declared_pks_and_declared_fks_removed'],
});
}
function writeFixture(config) {
const fixtureDir = path.join(fixtureRoot, config.id);
rmSync(fixtureDir, { recursive: true, force: true });
mkdirSync(fixtureDir, { recursive: true });
writeFileSync(path.join(fixtureDir, 'fixture.yaml'), fixtureYaml(config), 'utf8');
const dataPath = path.join(fixtureDir, 'data.sqlite');
const db = new Database(dataPath);
try {
db.pragma('foreign_keys = OFF');
db.exec(config.sql);
const { snapshot } = buildBenchmarkSnapshot({ db, fixtureId: config.id });
writeFixtureFiles({ fixtureDir, snapshot, expected: config.expected });
} finally {
db.close();
}
if (config.compressArtifacts) {
for (const fileName of ['snapshot.json', 'data.sqlite']) {
const rawPath = path.join(fixtureDir, fileName);
writeFileSync(`${rawPath}.gz`, gzipSync(readFileSync(rawPath)), 'utf8');
unlinkSync(rawPath);
}
}
console.log(`[built] ${config.id}: ${config.expected.expectedPks.length} PKs, ${config.expected.expectedLinks.length} links`);
}
function nonEnglishFixture() {
return {
id: 'non_english_naming_no_declared_constraints',
name: 'Non-English naming fixture with no declared constraints',
tier: 'row_bearing',
sql: [
'CREATE TABLE kundenstamm (kundennummer TEXT NOT NULL, firmenname TEXT NOT NULL, stadt TEXT NOT NULL);',
insertSql('kundenstamm', ['kundennummer', 'firmenname', 'stadt'], [
['K-001', 'Baeckerei Mueller', 'Muenchen'],
['K-002', 'Cafe Sakura', 'Berlin'],
['K-003', 'Nord Handel', 'Hamburg'],
]),
'CREATE TABLE bestellungen (bestellnummer TEXT NOT NULL, "kaeufer_nummer" TEXT NOT NULL, betrag INTEGER NOT NULL);',
insertSql('bestellungen', ['bestellnummer', 'kaeufer_nummer', 'betrag'], [
['B-100', 'K-001', 420],
['B-101', 'K-002', 300],
['B-102', 'K-001', 125],
]),
'CREATE TABLE seihin (seihin_bango TEXT NOT NULL, bezeichnung TEXT NOT NULL, kategorie TEXT NOT NULL);',
insertSql('seihin', ['seihin_bango', 'bezeichnung', 'kategorie'], [
['S-01', 'ocha', 'drink'],
['S-02', 'pan', 'food'],
['S-03', 'miso', 'food'],
]),
'CREATE TABLE uriage (verkauf_nr TEXT NOT NULL, hinban TEXT NOT NULL, menge INTEGER NOT NULL);',
insertSql('uriage', ['verkauf_nr', 'hinban', 'menge'], [
['U-1', 'S-01', 7],
['U-2', 'S-02', 3],
['U-3', 'S-01', 5],
]),
].join('\n'),
expected: {
expectedPks: [
{ table: 'kundenstamm', columns: ['kundennummer'] },
{ table: 'seihin', columns: ['seihin_bango'] },
],
expectedLinks: [
{
fromTable: 'bestellungen',
fromColumns: ['kaeufer_nummer'],
toTable: 'kundenstamm',
toColumns: ['kundennummer'],
relationship: 'many_to_one',
},
{
fromTable: 'uriage',
fromColumns: ['hinban'],
toTable: 'seihin',
toColumns: ['seihin_bango'],
relationship: 'many_to_one',
},
],
},
};
}
function abbreviatedLegacyFixture() {
return {
id: 'abbreviated_legacy_no_declared_constraints',
name: 'Abbreviated legacy naming fixture with no declared constraints',
tier: 'row_bearing',
sql: [
'CREATE TABLE cust (cust_id TEXT NOT NULL, nm TEXT NOT NULL, stat_cd TEXT NOT NULL);',
insertSql('cust', ['cust_id', 'nm', 'stat_cd'], [
['C001', 'Acme', 'A'],
['C002', 'Globex', 'A'],
['C003', 'Initech', 'I'],
]),
'CREATE TABLE prod (prod_cd TEXT NOT NULL, prod_nm TEXT NOT NULL, cat_cd TEXT NOT NULL);',
insertSql('prod', ['prod_cd', 'prod_nm', 'cat_cd'], [
['P10', 'Seat', 'FURN'],
['P11', 'Desk', 'FURN'],
['P12', 'Lamp', 'HOME'],
]),
'CREATE TABLE ord_hdr (ord_id TEXT NOT NULL, cust_id TEXT NOT NULL, ord_dt TEXT NOT NULL);',
insertSql('ord_hdr', ['ord_id', 'cust_id', 'ord_dt'], [
['O900', 'C001', '2026-01-01'],
['O901', 'C001', '2026-01-02'],
['O902', 'C002', '2026-01-03'],
]),
'CREATE TABLE ord_ln (ln_id TEXT NOT NULL, ord_id TEXT NOT NULL, prod_cd TEXT NOT NULL, qty INTEGER NOT NULL);',
insertSql('ord_ln', ['ln_id', 'ord_id', 'prod_cd', 'qty'], [
['L1', 'O900', 'P10', 2],
['L2', 'O900', 'P12', 1],
['L3', 'O901', 'P11', 4],
]),
].join('\n'),
expected: {
expectedPks: [
{ table: 'cust', columns: ['cust_id'] },
{ table: 'ord_hdr', columns: ['ord_id'] },
{ table: 'prod', columns: ['prod_cd'] },
],
expectedLinks: [
{
fromTable: 'ord_hdr',
fromColumns: ['cust_id'],
toTable: 'cust',
toColumns: ['cust_id'],
relationship: 'many_to_one',
},
{
fromTable: 'ord_ln',
fromColumns: ['ord_id'],
toTable: 'ord_hdr',
toColumns: ['ord_id'],
relationship: 'many_to_one',
},
{
fromTable: 'ord_ln',
fromColumns: ['prod_cd'],
toTable: 'prod',
toColumns: ['prod_cd'],
relationship: 'many_to_one',
},
],
},
};
}
function analyticalWarehouseFixture() {
return {
id: 'analytical_warehouse_no_naming_convention',
name: 'Analytical warehouse fixture with no naming convention',
tier: 'row_bearing',
sql: [
'CREATE TABLE dim_signup_country (country_code TEXT NOT NULL, country_name TEXT NOT NULL, region_name TEXT NOT NULL);',
insertSql('dim_signup_country', ['country_code', 'country_name', 'region_name'], [
['US', 'United States', 'americas'],
['DE', 'Germany', 'emea'],
['JP', 'Japan', 'apac'],
]),
'CREATE TABLE dim_commercial_plan (plan_code TEXT NOT NULL, plan_family TEXT NOT NULL, sales_motion TEXT NOT NULL);',
insertSql('dim_commercial_plan', ['plan_code', 'plan_family', 'sales_motion'], [
['FREE', 'free', 'self_serve'],
['TEAM', 'team', 'sales_assisted'],
['ENT', 'enterprise', 'sales_led'],
]),
'CREATE TABLE mart_revenue_daily (revenue_event_key TEXT NOT NULL, signup_country_code TEXT NOT NULL, commercial_plan_code TEXT NOT NULL, booked_revenue INTEGER NOT NULL);',
insertSql(
'mart_revenue_daily',
['revenue_event_key', 'signup_country_code', 'commercial_plan_code', 'booked_revenue'],
[
['R1', 'US', 'TEAM', 200],
['R2', 'DE', 'ENT', 900],
['R3', 'US', 'FREE', 0],
],
),
'CREATE TABLE mart_activation_cohort (cohort_key TEXT NOT NULL, first_touch_country TEXT NOT NULL, purchased_plan TEXT NOT NULL, activated_accounts INTEGER NOT NULL);',
insertSql(
'mart_activation_cohort',
['cohort_key', 'first_touch_country', 'purchased_plan', 'activated_accounts'],
[
['C1', 'JP', 'TEAM', 7],
['C2', 'DE', 'ENT', 2],
['C3', 'US', 'FREE', 30],
],
),
].join('\n'),
expected: {
expectedPks: [
{ table: 'dim_commercial_plan', columns: ['plan_code'] },
{ table: 'dim_signup_country', columns: ['country_code'] },
],
expectedLinks: [
{
fromTable: 'mart_activation_cohort',
fromColumns: ['first_touch_country'],
toTable: 'dim_signup_country',
toColumns: ['country_code'],
relationship: 'many_to_one',
},
{
fromTable: 'mart_activation_cohort',
fromColumns: ['purchased_plan'],
toTable: 'dim_commercial_plan',
toColumns: ['plan_code'],
relationship: 'many_to_one',
},
{
fromTable: 'mart_revenue_daily',
fromColumns: ['commercial_plan_code'],
toTable: 'dim_commercial_plan',
toColumns: ['plan_code'],
relationship: 'many_to_one',
},
{
fromTable: 'mart_revenue_daily',
fromColumns: ['signup_country_code'],
toTable: 'dim_signup_country',
toColumns: ['country_code'],
relationship: 'many_to_one',
},
],
},
};
}
function mixedCaseFixture() {
return {
id: 'mixed_case_within_schema_no_declared_constraints',
name: 'Mixed case within schema fixture with no declared constraints',
tier: 'row_bearing',
sql: [
'CREATE TABLE CustomerAccount (AccountID TEXT NOT NULL, AccountName TEXT NOT NULL, accountTier TEXT NOT NULL);',
insertSql('CustomerAccount', ['AccountID', 'AccountName', 'accountTier'], [
['A-1', 'Acme', 'team'],
['A-2', 'Globex', 'enterprise'],
['A-3', 'Initech', 'free'],
]),
'CREATE TABLE subscriptionPlans (planId TEXT NOT NULL, display_name TEXT NOT NULL, BillingCadence TEXT NOT NULL);',
insertSql('subscriptionPlans', ['planId', 'display_name', 'BillingCadence'], [
['P-free', 'Free', 'none'],
['P-team', 'Team', 'monthly'],
['P-ent', 'Enterprise', 'annual'],
]),
'CREATE TABLE order_events (event_id TEXT NOT NULL, accountId TEXT NOT NULL, plan_id TEXT NOT NULL, amount INTEGER NOT NULL);',
insertSql('order_events', ['event_id', 'accountId', 'plan_id', 'amount'], [
['E1', 'A-1', 'P-team', 120],
['E2', 'A-2', 'P-ent', 1000],
['E3', 'A-1', 'P-free', 0],
]),
'CREATE TABLE InvoiceHeader (InvoiceID TEXT NOT NULL, CustomerAccountID TEXT NOT NULL, invoice_total INTEGER NOT NULL);',
insertSql('InvoiceHeader', ['InvoiceID', 'CustomerAccountID', 'invoice_total'], [
['I1', 'A-1', 120],
['I2', 'A-2', 1000],
['I3', 'A-1', 20],
]),
'CREATE TABLE line_items (line_item_id TEXT NOT NULL, invoice_id TEXT NOT NULL, skuCode TEXT NOT NULL);',
insertSql('line_items', ['line_item_id', 'invoice_id', 'skuCode'], [
['L1', 'I1', 'SKU1'],
['L2', 'I1', 'SKU2'],
['L3', 'I2', 'SKU3'],
]),
].join('\n'),
expected: {
expectedPks: [
{ table: 'CustomerAccount', columns: ['AccountID'] },
{ table: 'InvoiceHeader', columns: ['InvoiceID'] },
{ table: 'subscriptionPlans', columns: ['planId'] },
],
expectedLinks: [
{
fromTable: 'InvoiceHeader',
fromColumns: ['CustomerAccountID'],
toTable: 'CustomerAccount',
toColumns: ['AccountID'],
relationship: 'many_to_one',
},
{
fromTable: 'line_items',
fromColumns: ['invoice_id'],
toTable: 'InvoiceHeader',
toColumns: ['InvoiceID'],
relationship: 'many_to_one',
},
{
fromTable: 'order_events',
fromColumns: ['accountId'],
toTable: 'CustomerAccount',
toColumns: ['AccountID'],
relationship: 'many_to_one',
},
{
fromTable: 'order_events',
fromColumns: ['plan_id'],
toTable: 'subscriptionPlans',
toColumns: ['planId'],
relationship: 'many_to_one',
},
],
},
};
}
function polymorphicFixture() {
return {
id: 'polymorphic_partial_overlap_no_declared_constraints',
name: 'Polymorphic partial-overlap fixture with no declared constraints',
tier: 'row_bearing',
sql: [
'CREATE TABLE users (user_id TEXT NOT NULL, email TEXT NOT NULL, lifecycle TEXT NOT NULL);',
insertSql('users', ['user_id', 'email', 'lifecycle'], [
['U1', 'ada@example.com', 'active'],
['U2', 'grace@example.com', 'active'],
['U3', 'alan@example.com', 'inactive'],
]),
'CREATE TABLE organizations (organization_id TEXT NOT NULL, organization_name TEXT NOT NULL, market TEXT NOT NULL);',
insertSql('organizations', ['organization_id', 'organization_name', 'market'], [
['O1', 'Acme', 'midmarket'],
['O2', 'Globex', 'enterprise'],
['O3', 'Initech', 'smb'],
]),
'CREATE TABLE activity_events (event_id TEXT NOT NULL, entity_id TEXT NOT NULL, entity_type TEXT NOT NULL, action_name TEXT NOT NULL);',
insertSql('activity_events', ['event_id', 'entity_id', 'entity_type', 'action_name'], [
['E1', 'U1', 'user', 'login'],
['E2', 'O1', 'organization', 'workspace_created'],
['E3', 'U2', 'user', 'invite_sent'],
['E4', 'O2', 'organization', 'billing_updated'],
]),
].join('\n'),
expected: {
expectedPks: [
{ table: 'organizations', columns: ['organization_id'] },
{ table: 'users', columns: ['user_id'] },
],
expectedLinks: [
{
fromTable: 'activity_events',
fromColumns: ['entity_id'],
toTable: 'organizations',
toColumns: ['organization_id'],
relationship: 'many_to_one',
},
{
fromTable: 'activity_events',
fromColumns: ['entity_id'],
toTable: 'users',
toColumns: ['user_id'],
relationship: 'many_to_one',
},
],
},
};
}
function padded(value, width) {
return String(value).padStart(width, '0');
}
function scaleFixture() {
const statements = [];
const expectedPks = [];
const expectedLinks = [];
const dimensionCount = 20;
const factCount = 380;
for (let dim = 0; dim < dimensionCount; dim += 1) {
const dimId = padded(dim, 2);
const table = `dim_entity_${dimId}`;
const key = `entity_${dimId}_key`;
const columns = [key, ...Array.from({ length: 49 }, (_, index) => `attribute_${padded(index, 2)}`)];
statements.push(`CREATE TABLE ${q(table)} (${columns.map((column) => `${q(column)} TEXT NOT NULL`).join(', ')});`);
statements.push(
insertSql(
table,
columns,
Array.from({ length: 3 }, (_, rowIndex) => [
`D${dimId}-${rowIndex}`,
...Array.from({ length: 49 }, (_, attrIndex) => `dim${dimId}_attr${attrIndex}_${rowIndex}`),
]),
),
);
expectedPks.push({ table, columns: [key] });
}
for (let fact = 0; fact < factCount; fact += 1) {
const factId = padded(fact, 3);
const table = `fact_activity_${factId}`;
const referencedDims = Array.from({ length: 5 }, (_, offset) => (fact + offset) % dimensionCount);
const referenceColumns = referencedDims.map((dim) => `entity_${padded(dim, 2)}_key`);
const metricColumns = Array.from({ length: 44 }, (_, index) => `metric_${padded(index, 2)}`);
const columns = ['event_id', ...referenceColumns, ...metricColumns];
statements.push(
`CREATE TABLE ${q(table)} (${[
`${q('event_id')} TEXT NOT NULL`,
...referenceColumns.map((column) => `${q(column)} TEXT NOT NULL`),
...metricColumns.map((column) => `${q(column)} INTEGER NOT NULL`),
].join(', ')});`,
);
statements.push(
insertSql(
table,
columns,
Array.from({ length: 3 }, (_, rowIndex) => [
`F${factId}-${rowIndex}`,
...referencedDims.map((dim) => `D${padded(dim, 2)}-${rowIndex}`),
...metricColumns.map((_, metricIndex) => fact * 1000 + metricIndex * 10 + rowIndex),
]),
),
);
for (const dim of referencedDims) {
const dimId = padded(dim, 2);
expectedLinks.push({
fromTable: table,
fromColumns: [`entity_${dimId}_key`],
toTable: `dim_entity_${dimId}`,
toColumns: [`entity_${dimId}_key`],
relationship: 'many_to_one',
});
}
}
return {
id: 'scale_stress_no_declared_constraints',
name: 'Scale stress fixture with no declared constraints',
tier: 'row_bearing',
validationBudget: 800,
compressArtifacts: true,
sql: statements.join('\n'),
expected: { expectedPks, expectedLinks },
};
}
const fixtures = [
nonEnglishFixture(),
abbreviatedLegacyFixture(),
analyticalWarehouseFixture(),
mixedCaseFixture(),
polymorphicFixture(),
scaleFixture(),
];
for (const fixture of fixtures) {
writeFixture(fixture);
}

View file

@ -0,0 +1,213 @@
#!/usr/bin/env node
import { readdir, readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const codeExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py']);
const runtimeAssetPatterns = [/^packages\/[^/]+\/prompts\/.+\.md$/, /^packages\/[^/]+\/skills\/.+\.md$/];
const identifierSkipPrefixes = ['docs/', 'examples/', 'python/klo-sl/plans/', 'python/klo-sl/openspec/'];
const forbiddenIdentifierTerms = ['kae' + 'lio', 'Kae' + 'lio', 'KAE' + 'LIO_'];
const appImportPatterns = [
{
label: 'server source import',
pattern: /(?:from\s+['"][^'"]*|import\s*\(\s*['"][^'"]*|import\s+['"][^'"]*)(?:@server\/|server\/src|(?:\.\.\/)+server\/src)/,
},
{
label: 'frontend source import',
pattern: /(?:from\s+['"][^'"]*|import\s*\(\s*['"][^'"]*|import\s+['"][^'"]*)(?:@frontend\/|frontend\/src|(?:\.\.\/)+frontend\/src)/,
},
{
label: 'python service app import',
pattern: /(?:from\s+['"][^'"]*|import\s*\(\s*['"][^'"]*|import\s+['"][^'"]*|from\s+)(?:python-service\/app|python_service\.app|app\.)/,
},
];
const llmBoundaryPatterns = [
{
label: 'direct Anthropic provider construction',
pattern: /\bcreateAnthropic\b/,
},
{
label: 'direct Vertex Anthropic provider construction',
pattern: /\bcreateVertexAnthropic\b/,
},
{
label: 'direct AI SDK gateway construction',
pattern: /\bcreateGateway\b/,
},
{
label: 'direct AI SDK embedding execution',
pattern: /\bembedMany\b/,
},
{
label: 'legacy context LLM provider port',
pattern: /\bLlmProviderPort\b/,
},
{
label: 'legacy scan LLM provider port',
pattern: /\bKloScanLlmPort\b/,
},
{
label: 'legacy gateway LLM provider helper',
pattern: /\bcreateGatewayLlmProvider\b/,
},
];
const contextProductionLlmBoundaryPatterns = [
{
label: 'context getModelByName call',
pattern: /\.\s*getModelByName\s*\(/,
},
];
function normalizePath(filePath) {
return filePath.split(path.sep).join('/');
}
function isCodeSource(relativePath) {
return codeExtensions.has(path.extname(relativePath));
}
function isRuntimeAsset(relativePath) {
return runtimeAssetPatterns.some((pattern) => pattern.test(relativePath));
}
function scansForAppImports(relativePath) {
return isCodeSource(relativePath);
}
function scansForLlmBoundaries(relativePath) {
return isCodeSource(relativePath) && relativePath.startsWith('packages/context/src/');
}
function isTestSource(relativePath) {
return /(?:^|\/)[^/]+\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativePath);
}
function scansForContextProductionLlmBoundaries(relativePath) {
return scansForLlmBoundaries(relativePath) && !isTestSource(relativePath);
}
function scansForForbiddenIdentifiers(relativePath) {
return isCodeSource(relativePath) || isRuntimeAsset(relativePath);
}
function skipsIdentifierScan(relativePath) {
return identifierSkipPrefixes.some((prefix) => relativePath.startsWith(prefix));
}
export function scanFileContent(relativePath, content) {
const normalizedPath = normalizePath(relativePath);
const violations = [];
if (scansForAppImports(normalizedPath)) {
for (const appImportPattern of appImportPatterns) {
if (appImportPattern.pattern.test(content)) {
violations.push({
file: normalizedPath,
kind: 'app-import',
message: `Forbidden ${appImportPattern.label}`,
});
}
}
}
if (scansForLlmBoundaries(normalizedPath)) {
for (const llmBoundaryPattern of llmBoundaryPatterns) {
if (llmBoundaryPattern.pattern.test(content)) {
violations.push({
file: normalizedPath,
kind: 'llm-boundary',
message: `Forbidden ${llmBoundaryPattern.label}; use @klo/llm`,
});
}
}
}
if (scansForContextProductionLlmBoundaries(normalizedPath)) {
for (const llmBoundaryPattern of contextProductionLlmBoundaryPatterns) {
if (llmBoundaryPattern.pattern.test(content)) {
violations.push({
file: normalizedPath,
kind: 'llm-boundary',
message: `Forbidden ${llmBoundaryPattern.label}; use getModel(role) inside @klo/context`,
});
}
}
}
if (scansForForbiddenIdentifiers(normalizedPath) && !skipsIdentifierScan(normalizedPath)) {
for (const term of forbiddenIdentifierTerms) {
if (content.includes(term)) {
violations.push({
file: normalizedPath,
kind: 'identifier',
message: `Forbidden product identifier "${term}"`,
});
}
}
}
return violations;
}
async function collectFiles(rootDir, currentDir = rootDir) {
const entries = await readdir(currentDir, { withFileTypes: true });
const files = [];
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.venv') {
continue;
}
files.push(...(await collectFiles(rootDir, fullPath)));
continue;
}
if (entry.isFile()) {
files.push(fullPath);
}
}
return files;
}
export async function collectViolations(rootDir) {
const files = await collectFiles(rootDir);
const violations = [];
for (const file of files) {
const relativePath = normalizePath(path.relative(rootDir, file));
const content = await readFile(file, 'utf8');
violations.push(...scanFileContent(relativePath, content));
}
return violations;
}
async function main() {
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(scriptDir, '..');
const violations = await collectViolations(rootDir);
if (violations.length === 0) {
process.stdout.write('klo boundary check passed\n');
return;
}
for (const violation of violations) {
process.stderr.write(`${violation.file}: ${violation.message}\n`);
}
process.exitCode = 1;
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
await main();
}

View file

@ -0,0 +1,147 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { scanFileContent } from './check-boundaries.mjs';
function productName() {
return ['Kae', 'lio'].join('');
}
function lowerProductName() {
return ['kae', 'lio'].join('');
}
describe('scanFileContent', () => {
it('rejects source imports from application directories', () => {
const serverAlias = '@' + 'server/contracts';
const pythonAppPath = 'python-service/' + 'app/api/endpoints/semantic_layer.py';
const violations = [
...scanFileContent('packages/context/src/index.ts', `import { orpc } from '${serverAlias}';`),
...scanFileContent('packages/context/src/index.ts', `import "${pythonAppPath}";`),
];
assert.deepEqual(
violations.map((violation) => violation.kind),
['app-import', 'app-import'],
);
});
it('rejects forbidden product identifiers in code source files', () => {
const violations = scanFileContent('packages/context/src/index.ts', `export const owner = '${lowerProductName()}';`);
assert.equal(violations.length, 1);
assert.equal(violations[0]?.kind, 'identifier');
});
it('rejects forbidden product identifiers in shipped runtime prompt assets', () => {
const violations = scanFileContent(
'packages/context/prompts/memory_agent_bundle_ingest_work_unit.md',
`Write output for ${productName()}.`,
);
assert.equal(violations.length, 1);
assert.equal(violations[0]?.kind, 'identifier');
assert.equal(violations[0]?.file, 'packages/context/prompts/memory_agent_bundle_ingest_work_unit.md');
});
it('rejects forbidden product identifiers in shipped runtime skill assets', () => {
const violations = scanFileContent(
'packages/context/skills/metabase_ingest/SKILL.md',
`Use ${productName()} project conventions.`,
);
assert.equal(violations.length, 1);
assert.equal(violations[0]?.kind, 'identifier');
assert.equal(violations[0]?.file, 'packages/context/skills/metabase_ingest/SKILL.md');
});
it('allows product identifiers in docs, examples, and transition metadata', () => {
const name = productName();
assert.equal(scanFileContent('docs/transition.md', name).length, 0);
assert.equal(scanFileContent('examples/transition.md', name).length, 0);
assert.equal(scanFileContent('python/klo-sl/plans/brainstorm.md', name).length, 0);
assert.equal(scanFileContent('python/klo-sl/openspec/specs/semantic-layer/spec.md', name).length, 0);
});
it('allows clean source files and clean runtime prompt assets', () => {
assert.deepEqual(
scanFileContent('packages/context/src/index.ts', "export const packageName = '@klo/context';"),
[],
);
assert.deepEqual(
scanFileContent('packages/context/prompts/memory_agent_bundle_ingest_work_unit.md', 'Write output for KLO.'),
[],
);
});
it('rejects context-owned LLM provider construction after @klo/llm migration', () => {
const violations = [
...scanFileContent(
'packages/context/src/agent/local-llm-provider.ts',
"import { createAnthropic } from '@ai-sdk/anthropic';",
),
...scanFileContent('packages/context/src/scan/local-ai-gateway-enrichment.ts', "import { createGateway } from 'ai';"),
...scanFileContent('packages/context/src/core/local-embedding-provider.ts', "import { embedMany } from 'ai';"),
];
assert.deepEqual(
violations.map((violation) => violation.kind),
['llm-boundary', 'llm-boundary', 'llm-boundary'],
);
});
it('rejects old KLO LLM port declarations in context', () => {
const violations = [
...scanFileContent('packages/context/src/agent/agent-runner.service.ts', 'export interface LlmProviderPort {}'),
...scanFileContent('packages/context/src/scan/types.ts', 'export interface KloScanLlmPort {}'),
...scanFileContent('packages/context/src/agent/gateway-llm-provider.ts', 'export function createGatewayLlmProvider() {}'),
];
assert.deepEqual(
violations.map((violation) => violation.kind),
['llm-boundary', 'llm-boundary', 'llm-boundary'],
);
});
it('rejects getModelByName calls in context production source', () => {
const violations = scanFileContent(
'packages/context/src/ingest/page-triage/page-triage.service.ts',
"const model = this.deps.llmProvider.getModelByName('claude-sonnet-4-6');",
);
assert.equal(violations.length, 1);
assert.equal(violations[0]?.kind, 'llm-boundary');
assert.equal(
violations[0]?.message,
'Forbidden context getModelByName call; use getModel(role) inside @klo/context',
);
});
it('allows role-driven getModel calls, test calls, and provider shape declarations', () => {
assert.deepEqual(
scanFileContent(
'packages/context/src/ingest/page-triage/page-triage.service.ts',
"const model = this.deps.llmProvider.getModel('triage');",
),
[],
);
assert.deepEqual(
scanFileContent(
'packages/context/src/ingest/page-triage/page-triage.service.test.ts',
"const model = this.deps.llmProvider.getModelByName('test-model');",
),
[],
);
assert.deepEqual(
scanFileContent(
'packages/context/src/scan/local-enrichment.ts',
'return { getModel() { return model; }, getModelByName() { return model; } };',
),
[],
);
});
});

View file

@ -0,0 +1,70 @@
import assert from 'node:assert/strict';
import { access, readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, it } from 'node:test';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
const ciWorkflowPath = resolve(repoRoot, '.github', 'workflows', 'ci.yml');
async function readCiWorkflowOrSkip(testContext) {
try {
await access(ciWorkflowPath);
} catch (error) {
if (error && error.code === 'ENOENT') {
testContext.skip('root CI workflow is absent from sparse klo checkout');
return null;
}
throw error;
}
return readFile(ciWorkflowPath, 'utf-8');
}
describe('KLO CI artifact upload contract', () => {
it('uploads verified KLO package artifacts from check-klo-subtree', async (testContext) => {
const workflow = await readCiWorkflowOrSkip(testContext);
if (workflow === null) {
return;
}
assert.match(
workflow,
/name: Build klo package artifacts and verify public smoke\s+run: cd klo && pnpm run artifacts:build && pnpm run artifacts:verify-manifest && pnpm run artifacts:verify-demo\s+- name: Upload klo package artifacts/s,
);
assert.match(workflow, /uses: actions\/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f/);
assert.match(workflow, /name: klo-package-artifacts-\$\{\{ github\.sha \}\}/);
assert.match(workflow, /klo\/dist\/artifacts\/manifest\.json/);
assert.match(workflow, /klo\/dist\/artifacts\/npm\/\*\.tgz/);
assert.match(workflow, /klo\/dist\/artifacts\/python\/\*\.whl/);
assert.match(workflow, /klo\/dist\/artifacts\/python\/\*\.tar\.gz/);
assert.match(workflow, /if-no-files-found: error/);
assert.match(workflow, /retention-days: 7/);
});
it('runs packed demo artifact smoke on Linux and macOS', async (testContext) => {
const workflow = await readCiWorkflowOrSkip(testContext);
if (workflow === null) {
return;
}
assert.match(workflow, /check-klo-packed-demo:/);
assert.match(workflow, /matrix:\s+os: \[ubuntu-latest, macos-latest\]/s);
assert.match(workflow, /name: Download klo package artifacts/);
assert.match(workflow, /path: klo\/dist\/artifacts/);
assert.match(workflow, /run: cd klo && pnpm run artifacts:verify-demo/);
});
it('includes packed demo artifact smoke in ci-success', async (testContext) => {
const workflow = await readCiWorkflowOrSkip(testContext);
if (workflow === null) {
return;
}
assert.match(
workflow,
/needs: \[check-klo-subtree, check-klo-packed-demo, build-python-service, test-server, build-frontend, run-pre-commit, build-docker-images\]/,
);
assert.match(workflow, /needs\.check-klo-packed-demo\.result.*== "failure"/);
assert.match(workflow, /needs\.check-klo-packed-demo\.result.*== "cancelled"/);
});
});

View file

@ -0,0 +1,174 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { describe, it } from 'node:test';
async function readText(relativePath) {
return readFile(new URL(`../${relativePath}`, import.meta.url), 'utf8');
}
describe('standalone example docs', () => {
it('documents the local warehouse example from the examples index', async () => {
const examples = await readText('examples/README.md');
assert.match(examples, /local-warehouse/);
assert.match(examples, /fake ingest adapter/);
assert.doesNotMatch(examples, /will contain standalone examples/);
});
it('documents the Orbit relationship verification example project', async () => {
const examples = await readText('examples/README.md');
const readme = await readText('examples/orbit-relationship-verification/README.md');
const config = await readText('examples/orbit-relationship-verification/klo.yaml');
assert.match(examples, /orbit-relationship-verification/);
assert.match(examples, /relationships:verify-orbit/);
assert.match(readme, /Orbit-style relationship discovery verification/);
assert.match(readme, /pnpm run relationships:verify-orbit/);
assert.match(readme, /Accepted: 9/);
assert.match(readme, /Review: 0/);
assert.match(readme, /Rejected: 0/);
assert.match(config, /project: orbit-relationship-verification/);
assert.match(config, /orbit:/);
assert.match(config, /driver: sqlite/);
assert.match(
config,
/path: \.\.\/\.\.\/packages\/context\/test\/fixtures\/relationship-benchmarks\/orbit_style_product_no_declared_constraints\/data\.sqlite/,
);
assert.match(config, /readonly: true/);
assert.match(config, /llm_proposals: false/);
assert.match(config, /validation_required_for_manifest: true/);
});
it('documents the Postgres historic SQL smoke example', async () => {
const examples = await readText('examples/README.md');
const readme = await readText('examples/postgres-historic/README.md');
const compose = await readText('examples/postgres-historic/docker-compose.yml');
const initSql = await readText('examples/postgres-historic/init/001-schema.sql');
const workload = await readText('examples/postgres-historic/scripts/generate-workload.sh');
const smoke = await readText('examples/postgres-historic/scripts/smoke.sh');
assert.match(examples, /postgres-historic/);
assert.match(examples, /pg_stat_statements/);
assert.match(readme, /--enable-historic-sql/);
assert.match(readme, /--historic-sql-min-calls 2/);
assert.match(readme, /klo dev doctor --project-dir/);
assert.match(readme, /Postgres Historic SQL/);
assert.match(readme, /dev ingest run/);
assert.match(compose, /postgres:14/);
assert.match(compose, /shared_preload_libraries=pg_stat_statements/);
assert.match(compose, /pg_stat_statements.track=top/);
assert.match(initSql, /CREATE EXTENSION IF NOT EXISTS pg_stat_statements/);
assert.match(initSql, /GRANT pg_read_all_stats TO klo_reader/);
assert.match(workload, /JOIN customers/);
assert.match(workload, /app_user/);
assert.match(workload, /etl_user/);
assert.match(smoke, /pg_stat_statements_reset/);
assert.match(smoke, /assert_manifest "\$FIRST_MANIFEST" true/);
assert.match(smoke, /assert_manifest "\$SECOND_MANIFEST" false/);
assert.match(smoke, /assert_manifest "\$RESET_MANIFEST" true/);
});
it('lists every published TypeScript package in the package root README', async () => {
const rootReadme = await readText('README.md');
assert.match(rootReadme, /`packages\/context`/);
assert.match(rootReadme, /`packages\/cli`/);
assert.match(rootReadme, /`packages\/connector-bigquery`/);
assert.match(rootReadme, /`packages\/connector-clickhouse`/);
assert.match(rootReadme, /`packages\/connector-mysql`/);
assert.match(rootReadme, /`packages\/connector-postgres`/);
assert.match(rootReadme, /`packages\/connector-posthog`/);
assert.match(rootReadme, /`packages\/connector-snowflake`/);
assert.match(rootReadme, /`packages\/connector-sqlite`/);
assert.match(rootReadme, /`packages\/connector-sqlserver`/);
assert.match(rootReadme, /`python\/klo-sl`/);
assert.match(rootReadme, /`python\/klo-daemon`/);
});
it('documents every standalone MCP tool that the CLI server exposes', async () => {
const rootReadme = await readText('README.md');
assert.match(rootReadme, /`connection_list`/);
assert.match(rootReadme, /`knowledge_search`/);
assert.match(rootReadme, /`knowledge_read`/);
assert.match(rootReadme, /`knowledge_write`/);
assert.match(rootReadme, /`sl_list_sources`/);
assert.match(rootReadme, /`sl_read_source`/);
assert.match(rootReadme, /`sl_write_source`/);
assert.match(rootReadme, /`sl_validate`/);
assert.match(rootReadme, /`sl_query`/);
assert.match(rootReadme, /`ingest_trigger`/);
assert.match(rootReadme, /`ingest_status`/);
assert.match(rootReadme, /`ingest_report`/);
assert.match(rootReadme, /`ingest_replay`/);
});
it('walks through klo connection list and klo connection test in the README quickstart', async () => {
const rootReadme = await readText('README.md');
assert.match(rootReadme, /connection list --project-dir/);
assert.match(rootReadme, /connection test warehouse --project-dir/);
assert.match(rootReadme, /Driver: sqlite/);
assert.match(rootReadme, /Tables: 1/);
});
it('replaces the fake-ingest smoke with a klo scan walkthrough in the README', async () => {
const rootReadme = await readText('README.md');
assert.match(rootReadme, /### Scan the demo warehouse/);
assert.match(rootReadme, /scan warehouse --project-dir/);
assert.match(rootReadme, /scan status --project-dir/);
assert.match(rootReadme, /scan report --project-dir/);
assert.match(rootReadme, /raw-sources\/warehouse\/live-database/);
assert.doesNotMatch(rootReadme, /Run a local ingest smoke test/);
assert.doesNotMatch(rootReadme, /klo dev ingest run --project-dir/);
assert.doesNotMatch(rootReadme, /klo ingest status --project-dir/);
});
it('documents pnpm setup as a prerequisite when optional dev linking fails', async () => {
const rootReadme = await readText('README.md');
assert.match(rootReadme, /pnpm run link:dev/);
assert.match(rootReadme, /klo-dev --help/);
assert.doesNotMatch(
rootReadme,
/If the setup command reports that pnpm's global bin directory is not on your\n`PATH`, add the printed directory to your shell profile/,
);
});
it('runs the example smoke in the cli smoke script', async () => {
const packageJson = JSON.parse(await readText('packages/cli/package.json'));
assert.match(packageJson.scripts.smoke, /src\/standalone-smoke\.test\.ts/);
assert.match(packageJson.scripts.smoke, /src\/example-smoke\.test\.ts/);
});
it('documents daemon HTTP database, source generation, LookML, embedding, and code execution support', async () => {
const readme = await readText('python/klo-daemon/README.md');
assert.match(readme, /semantic-generate-sources/);
assert.match(readme, /database-introspect/);
assert.match(readme, /POST \/database\/introspect/);
assert.match(readme, /Introspect a Postgres database schema/);
assert.match(readme, /lookml-parse/);
assert.match(readme, /embedding-compute/);
assert.match(readme, /embedding-compute-bulk/);
assert.match(readme, /code-execute/);
assert.match(readme, /--enable-code-execution/);
assert.match(readme, /POST \/semantic-layer\/generate-sources/);
assert.match(readme, /POST \/lookml\/parse/);
assert.match(readme, /POST \/embeddings\/compute/);
assert.match(readme, /POST \/embeddings\/compute-bulk/);
assert.match(readme, /POST \/code\/execute/);
assert.match(readme, /Generate semantic-layer sources from schema scan data/);
assert.match(readme, /Parse LookML projects into resolved, KSL-ready structures/);
assert.match(readme, /Compute text embeddings locally/);
assert.match(readme, /Execute Python code with the current in-process boundary/);
assert.match(readme, /Code execution is off by default/);
assert.match(readme, /does not provide OS-level sandboxing/);
assert.doesNotMatch(readme, /source generation are not exposed through this/);
assert.doesNotMatch(readme, /LookML parsing are not exposed through this/);
assert.doesNotMatch(readme, /embeddings are not exposed through this server mode/);
assert.doesNotMatch(readme, /Code execution is not exposed through this server mode/);
});
});

View file

@ -0,0 +1,432 @@
#!/usr/bin/env node
import { execFile, spawn } from 'node:child_process';
import { once } from 'node:events';
import { access, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { request as httpRequest } from 'node:http';
import { createServer } from 'node:net';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
import {
findPythonArtifacts,
npmSmokePackageJson,
npmSmokePythonEnv,
packageArtifactLayout,
pythonArtifactInstallArgs,
} from './package-artifacts.mjs';
const POSTGRES_IMAGE = process.env.KLO_ARTIFACT_POSTGRES_IMAGE ?? 'postgres:16-alpine';
const POSTGRES_USER = 'klo';
const POSTGRES_PASSWORD = 'postgres'; // pragma: allowlist secret
const POSTGRES_DB = 'warehouse';
export function smokeContainerName(pid = process.pid, now = Date.now()) {
return `klo-live-db-smoke-${pid}-${now}`;
}
export function buildPostgresUrl(hostPort) {
return `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:${hostPort}/${POSTGRES_DB}`; // pragma: allowlist secret
}
export function buildDockerRunArgs({ containerName, hostPort, image = POSTGRES_IMAGE }) {
return [
'run',
'--rm',
'-d',
'--name',
containerName,
'-e',
`POSTGRES_PASSWORD=${POSTGRES_PASSWORD}`,
'-e',
`POSTGRES_USER=${POSTGRES_USER}`,
'-e',
`POSTGRES_DB=${POSTGRES_DB}`,
'-p',
`127.0.0.1:${hostPort}:5432`,
image,
];
}
export function buildPostgresReadyArgs(containerName) {
return [
'exec',
containerName,
'psql',
'-U',
POSTGRES_USER,
'-d',
POSTGRES_DB,
'-v',
'ON_ERROR_STOP=1',
'-c',
'SELECT 1;',
];
}
export function buildSeedSql() {
return [
'DROP TABLE IF EXISTS orders;',
'DROP TABLE IF EXISTS customers;',
'CREATE TABLE customers (',
' id integer PRIMARY KEY,',
' name text NOT NULL',
');',
"COMMENT ON TABLE customers IS 'Customers captured by the artifact smoke';",
"COMMENT ON COLUMN customers.name IS 'Customer display name';",
'CREATE TABLE orders (',
' id integer PRIMARY KEY,',
' customer_id integer NOT NULL REFERENCES customers(id),',
' status text NOT NULL,',
' amount integer NOT NULL',
');',
"COMMENT ON TABLE orders IS 'Orders captured by the artifact smoke';",
"COMMENT ON COLUMN orders.amount IS 'Order amount in cents';",
"INSERT INTO customers (id, name) VALUES (1, 'Acme'), (2, 'Globex');",
"INSERT INTO orders (id, customer_id, status, amount) VALUES (10, 1, 'paid', 2000), (11, 2, 'open', 3500);",
'',
].join('\n');
}
export function buildKloYaml(postgresUrl) {
return [
'project: artifact-live-database',
'connections:',
' warehouse:',
' driver: postgres',
` url: "${postgresUrl}"`,
' readonly: true',
'storage:',
' state: sqlite',
' search: sqlite-fts5',
'ingest:',
' adapters:',
' - live-database',
'',
].join('\n');
}
export function buildLiveDatabaseIngestArgs(projectDir, databaseIntrospectionUrl) {
return [
'exec',
'klo',
'dev',
'ingest',
'run',
'--project-dir',
projectDir,
'--connection-id',
'warehouse',
'--adapter',
'live-database',
'--database-introspection-url',
databaseIntrospectionUrl,
];
}
export function buildLiveDatabaseStatusArgs(projectDir, runId) {
return ['exec', 'klo', 'ingest', 'status', '--project-dir', projectDir, runId];
}
async function run(command, args, options = {}) {
process.stdout.write(`$ ${command} ${args.join(' ')}\n`);
return new Promise((resolve) => {
const child = execFile(
command,
args,
{
cwd: options.cwd,
env: options.env ?? process.env,
encoding: 'utf8',
maxBuffer: 1024 * 1024 * 20,
timeout: options.timeout ?? 60_000,
},
(error, stdout, stderr) => {
if (stdout) {
process.stdout.write(stdout);
}
if (stderr) {
process.stderr.write(stderr);
}
resolve({
code: error && typeof error.code === 'number' ? error.code : error ? 1 : 0,
stdout,
stderr: stderr || (error instanceof Error ? error.message : ''),
});
},
);
if (options.input !== undefined) {
child.stdin?.end(options.input);
}
});
}
function requireSuccess(label, result) {
if (result.code !== 0) {
throw new Error(
`${label} failed with code ${result.code}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
);
}
}
function requireOutput(label, result, pattern) {
if (!pattern.test(result.stdout)) {
throw new Error(`${label} output did not match ${pattern}\nstdout:\n${result.stdout}`);
}
}
function getRunId(stdout) {
const match = stdout.match(/^Run: (.+)$/m);
if (!match) {
throw new Error(`ingest run output did not include a run id\nstdout:\n${stdout}`);
}
return match[1];
}
async function requireDocker() {
const result = await run('docker', ['info'], { timeout: 20_000 });
if (result.code !== 0) {
throw new Error(
'Docker is required for the installed live-database artifact smoke. Start Docker and rerun `pnpm run artifacts:live-db-smoke`.',
);
}
}
async function getAvailablePort() {
const server = createServer();
server.listen(0, '127.0.0.1');
await once(server, 'listening');
const address = server.address();
if (!address || typeof address === 'string') {
server.close();
throw new Error('expected TCP server address');
}
const port = address.port;
server.close();
await once(server, 'close');
return port;
}
async function startPostgresContainer(containerName, hostPort) {
await requireDocker();
const result = await run('docker', buildDockerRunArgs({ containerName, hostPort }), { timeout: 120_000 });
requireSuccess('docker run postgres', result);
}
async function stopPostgresContainer(containerName) {
await run('docker', ['rm', '-f', containerName], { timeout: 30_000 });
}
async function waitForPostgres(containerName) {
const deadline = Date.now() + 60_000;
while (Date.now() < deadline) {
const result = await run('docker', buildPostgresReadyArgs(containerName), { timeout: 10_000 });
if (result.code === 0) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 500));
}
throw new Error(`Timed out waiting for Postgres container ${containerName}`);
}
async function seedPostgres(containerName) {
const result = await run(
'docker',
['exec', '-i', containerName, 'psql', '-U', POSTGRES_USER, '-d', POSTGRES_DB, '-v', 'ON_ERROR_STOP=1'],
{ input: buildSeedSql(), timeout: 30_000 },
);
requireSuccess('seed postgres catalog', result);
}
function httpGetOk(url) {
return new Promise((resolve, reject) => {
const request = httpRequest(url, { method: 'GET' }, (response) => {
response.resume();
response.on('end', () => resolve((response.statusCode ?? 0) >= 200 && (response.statusCode ?? 0) < 300));
});
request.on('error', reject);
request.end();
});
}
function spawnLogged(command, args, options = {}) {
const stdout = [];
const stderr = [];
let spawnError;
const child = spawn(command, args, {
cwd: options.cwd,
env: options.env ?? process.env,
stdio: ['ignore', 'pipe', 'pipe'],
});
child.stdout.on('data', (chunk) => stdout.push(chunk));
child.stderr.on('data', (chunk) => stderr.push(chunk));
child.on('error', (error) => {
spawnError = error;
});
return {
child,
error() {
return spawnError;
},
output() {
return {
stdout: Buffer.concat(stdout).toString('utf8'),
stderr: Buffer.concat(stderr).toString('utf8'),
};
},
};
}
async function waitForHttpHealth(url, daemon) {
const deadline = Date.now() + 15_000;
while (Date.now() < deadline) {
if (daemon.error()) {
const output = daemon.output();
throw new Error(
`Failed to start klo-daemon: ${daemon.error().message}\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`,
);
}
if (daemon.child.exitCode !== null || daemon.child.signalCode !== null) {
const output = daemon.output();
throw new Error(`klo-daemon exited before health check passed\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`);
}
try {
if (await httpGetOk(url)) {
return;
}
} catch {
await new Promise((resolve) => setTimeout(resolve, 100));
continue;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
const output = daemon.output();
throw new Error(`Timed out waiting for ${url}\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`);
}
async function startDaemon(port, cleanInstallDir) {
const daemon = spawnLogged(
'klo-daemon',
['serve-http', '--host', '127.0.0.1', '--port', String(port), '--log-level', 'warning'],
{ cwd: cleanInstallDir, env: npmSmokePythonEnv(cleanInstallDir) },
);
await waitForHttpHealth(`http://127.0.0.1:${port}/health`, daemon);
return daemon;
}
async function stopDaemon(daemon) {
if (daemon.child.exitCode !== null || daemon.child.signalCode !== null) {
return;
}
daemon.child.kill('SIGTERM');
const closed = once(daemon.child, 'close').then(() => true);
const timedOut = new Promise((resolve) => setTimeout(() => resolve(false), 5_000));
if (!(await Promise.race([closed, timedOut]))) {
daemon.child.kill('SIGKILL');
await once(daemon.child, 'close');
}
}
async function assertPathExists(path, label) {
try {
await access(path);
} catch {
throw new Error(`Missing ${label}: ${path}`);
}
}
async function prepareCleanInstall(layout, cleanInstallDir) {
const pythonArtifacts = await findPythonArtifacts(layout.pythonDir);
await assertPathExists(layout.contextTarball, '@klo/context tarball');
await assertPathExists(layout.cliTarball, '@klo/cli tarball');
await mkdir(cleanInstallDir, { recursive: true });
await writeFile(join(cleanInstallDir, 'package.json'), `${JSON.stringify(npmSmokePackageJson(layout), null, 2)}\n`);
await run('pnpm', ['install'], { cwd: cleanInstallDir, timeout: 120_000 }).then((result) =>
requireSuccess('pnpm install clean artifact project', result),
);
await run('uv', ['venv', '.venv'], { cwd: cleanInstallDir, timeout: 120_000 }).then((result) =>
requireSuccess('uv venv clean artifact project', result),
);
await run(
'uv',
pythonArtifactInstallArgs(
join(cleanInstallDir, '.venv', process.platform === 'win32' ? 'Scripts/python.exe' : 'bin/python'),
pythonArtifacts,
),
{
cwd: cleanInstallDir,
timeout: 120_000,
},
).then((result) => requireSuccess('install Python artifacts', result));
}
async function main() {
const layout = packageArtifactLayout();
const root = await mkdtemp(join(tmpdir(), 'klo-live-db-artifact-smoke-'));
const containerName = smokeContainerName();
let daemon;
try {
const postgresPort = await getAvailablePort();
const daemonPort = await getAvailablePort();
const postgresUrl = buildPostgresUrl(postgresPort);
const cleanInstallDir = join(root, 'npm-clean-install');
const projectDir = join(root, 'project');
const databaseIntrospectionUrl = `http://127.0.0.1:${daemonPort}`;
await startPostgresContainer(containerName, postgresPort);
await waitForPostgres(containerName);
await seedPostgres(containerName);
await prepareCleanInstall(layout, cleanInstallDir);
await mkdir(projectDir, { recursive: true });
const init = await run('pnpm', ['exec', 'klo', 'init', projectDir, '--name', 'artifact-live-database'], {
cwd: cleanInstallDir,
timeout: 30_000,
});
requireSuccess('klo init', init);
await writeFile(join(projectDir, 'klo.yaml'), buildKloYaml(postgresUrl), 'utf8');
daemon = await startDaemon(daemonPort, cleanInstallDir);
const ingestRun = await run('pnpm', buildLiveDatabaseIngestArgs(projectDir, databaseIntrospectionUrl), {
cwd: cleanInstallDir,
env: npmSmokePythonEnv(cleanInstallDir),
timeout: 120_000,
});
requireSuccess('klo dev ingest run live-database', ingestRun);
requireOutput('klo dev ingest run live-database', ingestRun, /Status: done/);
requireOutput('klo dev ingest run live-database', ingestRun, /Adapter: live-database/);
requireOutput('klo dev ingest run live-database', ingestRun, /Diff: \+4\/~0\/-0\/=0/);
requireOutput('klo dev ingest run live-database', ingestRun, /Raw files: 4/);
requireOutput('klo dev ingest run live-database', ingestRun, /Work units: 2/);
const runId = getRunId(ingestRun.stdout);
const ingestStatus = await run('pnpm', buildLiveDatabaseStatusArgs(projectDir, runId), {
cwd: cleanInstallDir,
env: npmSmokePythonEnv(cleanInstallDir),
timeout: 30_000,
});
requireSuccess('klo ingest status live-database', ingestStatus);
requireOutput('klo ingest status live-database', ingestStatus, new RegExp(`Run: ${runId}`));
requireOutput('klo ingest status live-database', ingestStatus, /Status: done/);
requireOutput('klo ingest status live-database', ingestStatus, /Raw files: 4/);
requireOutput('klo ingest status live-database', ingestStatus, /Work units: 2/);
await assertPathExists(join(projectDir, '.klo', 'db.sqlite'), 'SQLite local ingest state');
process.stdout.write(`Installed live-database artifact smoke passed: ${runId}\n`);
} finally {
if (daemon) {
await stopDaemon(daemon);
}
await stopPostgresContainer(containerName);
await rm(root, { recursive: true, force: true });
}
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
try {
await main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\n`);
process.exitCode = 1;
}
}

View file

@ -0,0 +1,128 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import {
buildDockerRunArgs,
buildKloYaml,
buildLiveDatabaseIngestArgs,
buildLiveDatabaseStatusArgs,
buildPostgresUrl,
buildPostgresReadyArgs,
buildSeedSql,
smokeContainerName,
} from './installed-live-database-smoke.mjs';
describe('installed live-database artifact smoke helpers', () => {
it('builds a deterministic disposable Postgres container command', () => {
assert.deepEqual(
buildDockerRunArgs({
containerName: 'klo-live-db-smoke-test',
hostPort: 15432,
image: 'postgres:16-alpine',
}),
[
'run',
'--rm',
'-d',
'--name',
'klo-live-db-smoke-test',
'-e',
'POSTGRES_PASSWORD=postgres', // pragma: allowlist secret
'-e',
'POSTGRES_USER=klo',
'-e',
'POSTGRES_DB=warehouse',
'-p',
'127.0.0.1:15432:5432',
'postgres:16-alpine',
],
);
});
it('uses a collision-resistant Docker container name prefix', () => {
assert.match(smokeContainerName(1234, 5678), /^klo-live-db-smoke-1234-5678$/);
});
it('builds the Postgres URL used by klo.yaml and daemon introspection', () => {
assert.equal(
buildPostgresUrl(15432),
'postgresql://klo:postgres@127.0.0.1:15432/warehouse', // pragma: allowlist secret
);
});
it('writes a live-database-only KLO project config with SQLite local state', () => {
assert.equal(
buildKloYaml('postgresql://klo:postgres@127.0.0.1:15432/warehouse'), // pragma: allowlist secret
[
'project: artifact-live-database',
'connections:',
' warehouse:',
' driver: postgres',
' url: "postgresql://klo:postgres@127.0.0.1:15432/warehouse"', // pragma: allowlist secret
' readonly: true',
'storage:',
' state: sqlite',
' search: sqlite-fts5',
'ingest:',
' adapters:',
' - live-database',
'',
].join('\n'),
);
});
it('seeds comments and a foreign key for daemon catalog introspection', () => {
const sql = buildSeedSql();
assert.match(sql, /CREATE TABLE customers/);
assert.match(sql, /CREATE TABLE orders/);
assert.match(sql, /REFERENCES customers\(id\)/);
assert.match(sql, /COMMENT ON TABLE orders IS 'Orders captured by the artifact smoke'/);
assert.match(sql, /COMMENT ON COLUMN orders.amount IS 'Order amount in cents'/);
assert.match(sql, /INSERT INTO orders/);
});
it('waits for a real SQL connection to the target Postgres database', () => {
assert.deepEqual(buildPostgresReadyArgs('klo-live-db-smoke-test'), [
'exec',
'klo-live-db-smoke-test',
'psql',
'-U',
'klo',
'-d',
'warehouse',
'-v',
'ON_ERROR_STOP=1',
'-c',
'SELECT 1;',
]);
});
it('builds installed CLI live-database ingest and status commands', () => {
assert.deepEqual(buildLiveDatabaseIngestArgs('/tmp/project', 'http://127.0.0.1:8765'), [
'exec',
'klo',
'dev',
'ingest',
'run',
'--project-dir',
'/tmp/project',
'--connection-id',
'warehouse',
'--adapter',
'live-database',
'--database-introspection-url',
'http://127.0.0.1:8765',
]);
assert.deepEqual(buildLiveDatabaseStatusArgs('/tmp/project', 'local-run-1'), [
'exec',
'klo',
'ingest',
'status',
'--project-dir',
'/tmp/project',
'local-run-1',
]);
});
});

197
scripts/link-dev-cli.mjs Normal file
View file

@ -0,0 +1,197 @@
#!/usr/bin/env node
import { execFile } from 'node:child_process';
import { constants } from 'node:fs';
import { access as fsAccess, chmod as fsChmod, writeFile as fsWriteFile } from 'node:fs/promises';
import { delimiter, join } from 'node:path';
import { pathToFileURL } from 'node:url';
import { promisify } from 'node:util';
import { ensureCliBinExecutable, kloRootDir } from './prepare-cli-bin.mjs';
const execFileAsync = promisify(execFile);
function hasFlag(flag) {
return process.argv.includes(flag);
}
function optionValue(flag, fallback) {
const index = process.argv.indexOf(flag);
if (index === -1) {
return fallback;
}
const value = process.argv[index + 1];
if (!value || value.startsWith('-')) {
throw new Error(`${flag} requires a value`);
}
return value;
}
function commandEnv(extraPath) {
if (!extraPath) {
return process.env;
}
return {
...process.env,
PATH: `${extraPath}${delimiter}${process.env.PATH ?? ''}`,
};
}
async function execText(command, args, options = {}) {
const result = await execFileAsync(command, args, {
cwd: options.cwd,
env: options.env,
maxBuffer: 1024 * 1024,
});
return `${result.stdout}${result.stderr}`.trim();
}
async function optionalText(command, args, options = {}) {
try {
return await execText(command, args, options);
} catch {
return '';
}
}
async function findPnpmGlobalBin() {
const output = await optionalText('pnpm', ['bin', '--global']);
return output.split(/\r?\n/).find((line) => line.trim().length > 0)?.trim() ?? '';
}
function shellDoubleQuote(value) {
return `"${value.replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('$', '\\$').replaceAll('`', '\\`')}"`;
}
function assertBinaryName(binaryName) {
if (!/^[A-Za-z][A-Za-z0-9._-]*$/.test(binaryName)) {
throw new Error(`Invalid binary name: ${binaryName}`);
}
}
async function writePinnedPosixLauncher(globalBin, binPath, binaryName, writeFile, chmod) {
const launcherPath = join(globalBin, binaryName);
const script = [
'#!/bin/sh',
'# Generated by `pnpm run link:dev` in the KLO workspace.',
'# Keep this launcher pinned to the Node binary that built native dependencies.',
`exec ${shellDoubleQuote(process.execPath)} ${shellDoubleQuote(binPath)} "$@"`,
'',
].join('\n');
await writeFile(launcherPath, script, 'utf-8');
await chmod(launcherPath, 0o755);
return launcherPath;
}
async function writePinnedWindowsLauncher(globalBin, binPath, binaryName, writeFile) {
const launcherPath = join(globalBin, `${binaryName}.cmd`);
const script = [
'@echo off',
'REM Generated by `pnpm run link:dev` in the KLO workspace.',
`"${process.execPath}" "${binPath}" %*`,
'',
].join('\r\n');
await writeFile(launcherPath, script, 'utf-8');
return launcherPath;
}
async function writePinnedLauncher(globalBin, binPath, binaryName, deps) {
if (!globalBin) {
throw new Error('Could not find pnpm global bin directory. Run `pnpm setup`, restart your shell, then retry.');
}
if (process.platform === 'win32') {
return writePinnedWindowsLauncher(globalBin, binPath, binaryName, deps.writeFile);
}
return writePinnedPosixLauncher(globalBin, binPath, binaryName, deps.writeFile, deps.chmod);
}
async function verifyBinaryOnPath(binaryName, globalBin, execTextFn) {
try {
const output = await execTextFn(binaryName, ['--version']);
return { ok: true, output };
} catch (error) {
if (!globalBin) {
return { ok: false, output: '', error };
}
try {
const output = await execTextFn(binaryName, ['--version'], { env: commandEnv(globalBin) });
return { ok: false, output, globalBin, error };
} catch {
return { ok: false, output: '', error };
}
}
}
async function assertBuiltCli(rootDir, access, binPathOverride) {
const binPath = binPathOverride ?? (await ensureCliBinExecutable(rootDir));
await access(binPath, constants.X_OK);
return binPath;
}
export async function linkDevCli(options = {}) {
const rootDir = options.rootDir ?? kloRootDir();
const binaryName = options.binaryName ?? 'klo-dev';
const access = options.access ?? fsAccess;
const chmod = options.chmod ?? fsChmod;
const writeFile = options.writeFile ?? fsWriteFile;
const execTextFn = options.execText ?? execText;
assertBinaryName(binaryName);
const binPath = await assertBuiltCli(rootDir, access, options.binPath);
const globalBin = options.globalBin ?? (await findPnpmGlobalBin());
if (options.checkOnly) {
return {
binaryName,
binPath,
linked: false,
verification: await verifyBinaryOnPath(binaryName, globalBin, execTextFn),
};
}
const launcherPath = await writePinnedLauncher(globalBin, binPath, binaryName, { writeFile, chmod });
const verification = await verifyBinaryOnPath(binaryName, globalBin, execTextFn);
if (!verification.ok) {
const pathHint = verification.globalBin
? `\nAdd pnpm's global bin directory to PATH, then retry:\n\n export PATH="${verification.globalBin}:$PATH"\n\n`
: '\nRun `pnpm setup`, restart your shell, then rerun `pnpm run link:dev`.\n\n';
throw new Error(`${binaryName} was linked at ${launcherPath}, but it is not available on PATH.${pathHint}`);
}
return {
binaryName,
binPath,
launcherPath,
linked: true,
verification,
};
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
const result = await linkDevCli({
checkOnly: hasFlag('--check-only'),
binaryName: optionValue('--name', 'klo-dev'),
});
process.stdout.write(`KLO CLI bin: ${result.binPath}\n`);
if (result.linked) {
process.stdout.write(`Linked binary: ${result.binaryName}\n`);
process.stdout.write(`Verified: ${result.verification.output}\n`);
process.stdout.write(`Pinned Node: ${process.execPath} ${process.version} ABI ${process.versions.modules}\n`);
process.stdout.write(`You can now run \`${result.binaryName} --help\` from any directory.\n`);
} else if (result.verification.ok) {
process.stdout.write(`Already available: ${result.verification.output}\n`);
} else {
process.stdout.write(`${result.binaryName} is not linked on PATH yet.\n`);
}
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}
}

View file

@ -0,0 +1,45 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { linkDevCli } from './link-dev-cli.mjs';
test('linkDevCli writes a klo-dev launcher by default', async () => {
const writes = [];
const chmods = [];
const result = await linkDevCli({
rootDir: '/workspace/klo',
globalBin: '/pnpm/bin',
binPath: '/workspace/klo/packages/cli/dist/bin.js',
execText: async (command, args) => {
assert.equal(command, 'klo-dev');
assert.deepEqual(args, ['--version']);
return '@klo/cli 0.0.0-private';
},
writeFile: async (path, content) => writes.push({ path, content }),
chmod: async (path, mode) => chmods.push({ path, mode }),
access: async () => undefined,
});
assert.equal(result.binaryName, 'klo-dev');
assert.equal(writes[0].path, '/pnpm/bin/klo-dev');
assert.match(writes[0].content, /packages\/cli\/dist\/bin.js/);
assert.deepEqual(chmods, [{ path: '/pnpm/bin/klo-dev', mode: 0o755 }]);
});
test('linkDevCli can explicitly write klo when requested', async () => {
const writes = [];
const result = await linkDevCli({
rootDir: '/workspace/klo',
binaryName: 'klo',
globalBin: '/pnpm/bin',
binPath: '/workspace/klo/packages/cli/dist/bin.js',
execText: async () => '@klo/cli 0.0.0-private',
writeFile: async (path, content) => writes.push({ path, content }),
chmod: async () => undefined,
access: async () => undefined,
});
assert.equal(result.binaryName, 'klo');
assert.equal(writes[0].path, '/pnpm/bin/klo');
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,655 @@
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, it } from 'node:test';
import {
artifactManifestPath,
buildArtifactCommands,
findPythonArtifacts,
NPM_ARTIFACT_PACKAGES,
npmDemoSmokeSource,
npmRuntimeSmokeSource,
npmSmokePackageJson,
npmSmokePythonEnv,
npmVerifySource,
packageArtifactLayout,
packageReleaseMetadata,
pythonArtifactInstallArgs,
pythonVerifySource,
verifyArtifactManifest,
writeArtifactManifest,
} from './package-artifacts.mjs';
const STALE_METABASE_UNSUPPORTED = ['Standalone Metabase scheduled fetch', 'is intentionally unsupported'].join(' ');
async function writeJson(path, value) {
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
}
const CONNECTOR_PACKAGE_NAMES = [
'@klo/connector-bigquery',
'@klo/connector-clickhouse',
'@klo/connector-mysql',
'@klo/connector-postgres',
'@klo/connector-posthog',
'@klo/connector-snowflake',
'@klo/connector-sqlite',
'@klo/connector-sqlserver',
];
function packageRootForName(packageName) {
return `packages/${packageName.replace('@klo/', '')}`;
}
function expectedNpmArtifactPath(packageName) {
return `npm/${packageName.replace('@klo/', 'klo-')}-0.0.0-private.tgz`;
}
async function writeReleaseMetadataInputs(root) {
const npmPackages = ['@klo/context', '@klo/llm', ...CONNECTOR_PACKAGE_NAMES, '@klo/cli'];
for (const packageName of npmPackages) {
const packageRoot = packageName === '@klo/context' ? 'packages/context' : packageRootForName(packageName);
await mkdir(join(root, packageRoot), { recursive: true });
await writeJson(join(root, packageRoot, 'package.json'), {
name: packageName,
version: '0.0.0-private',
private: true,
});
}
await mkdir(join(root, 'python', 'klo-sl'), { recursive: true });
await mkdir(join(root, 'python', 'klo-daemon'), { recursive: true });
await writeFile(
join(root, 'python', 'klo-sl', 'pyproject.toml'),
['[project]', 'name = "klo-sl"', 'version = "0.1.0"', ''].join('\n'),
);
await writeFile(
join(root, 'python', 'klo-daemon', 'pyproject.toml'),
['[project]', 'name = "klo-daemon"', 'version = "0.1.0"', ''].join('\n'),
);
}
async function writeUploadableArtifactFixtures(layout) {
await mkdir(layout.npmDir, { recursive: true });
await mkdir(layout.pythonDir, { recursive: true });
const fileContents = new Map([
...NPM_ARTIFACT_PACKAGES.map((packageInfo) => [
layout.npmTarballs[packageInfo.name],
`${packageInfo.name}-tarball`,
]),
[join(layout.pythonDir, 'klo_sl-0.1.0-py3-none-any.whl'), 'klo-sl-wheel'],
[join(layout.pythonDir, 'klo_sl-0.1.0.tar.gz'), 'klo-sl-sdist'],
[join(layout.pythonDir, 'klo_daemon-0.1.0-py3-none-any.whl'), 'klo-daemon-wheel'],
[join(layout.pythonDir, 'klo_daemon-0.1.0.tar.gz'), 'klo-daemon-sdist'],
]);
for (const [path, contents] of fileContents) {
await writeFile(path, contents);
}
}
describe('packageArtifactLayout', () => {
it('uses stable artifact paths under klo/dist/artifacts', () => {
const layout = packageArtifactLayout('/repo/klo');
assert.equal(layout.artifactDir, '/repo/klo/dist/artifacts');
assert.equal(layout.npmDir, '/repo/klo/dist/artifacts/npm');
assert.equal(layout.pythonDir, '/repo/klo/dist/artifacts/python');
assert.equal(layout.contextTarball, '/repo/klo/dist/artifacts/npm/klo-context-0.0.0-private.tgz');
assert.equal(layout.cliTarball, '/repo/klo/dist/artifacts/npm/klo-cli-0.0.0-private.tgz');
assert.equal(
layout.connectorTarballs['@klo/connector-sqlite'],
'/repo/klo/dist/artifacts/npm/klo-connector-sqlite-0.0.0-private.tgz',
);
assert.equal(
layout.connectorTarballs['@klo/connector-postgres'],
'/repo/klo/dist/artifacts/npm/klo-connector-postgres-0.0.0-private.tgz',
);
assert.deepEqual(
Object.keys(layout.npmTarballs),
NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name),
);
});
});
describe('buildArtifactCommands', () => {
it('builds all TypeScript packages before packing npm artifacts and builds both Python packages', () => {
const layout = packageArtifactLayout('/repo/klo');
const commands = buildArtifactCommands(layout);
assert.deepEqual(
commands.slice(0, NPM_ARTIFACT_PACKAGES.length).map((command) => [command.command, command.args]),
NPM_ARTIFACT_PACKAGES.map((packageInfo) => ['pnpm', ['--filter', packageInfo.name, 'run', 'build']]),
);
assert.deepEqual(
commands
.slice(NPM_ARTIFACT_PACKAGES.length, NPM_ARTIFACT_PACKAGES.length * 2)
.map((command) => [command.command, command.args]),
NPM_ARTIFACT_PACKAGES.map((packageInfo) => [
'pnpm',
['--filter', packageInfo.name, 'pack', '--out', layout.npmTarballs[packageInfo.name]],
]),
);
assert.deepEqual(
commands.slice(NPM_ARTIFACT_PACKAGES.length * 2).map((command) => [command.command, command.args]),
[
['uv', ['build', '--package', 'klo-sl', '--out-dir', '/repo/klo/dist/artifacts/python']],
['uv', ['build', '--package', 'klo-daemon', '--out-dir', '/repo/klo/dist/artifacts/python']],
],
);
});
});
describe('packageReleaseMetadata', () => {
it('reads package identities and versions from package manifests', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-metadata-test-'));
try {
await writeReleaseMetadataInputs(root);
assert.deepEqual(await packageReleaseMetadata(root), [
...NPM_ARTIFACT_PACKAGES.map((packageInfo) => ({
ecosystem: 'npm',
packageName: packageInfo.name,
packageRoot: packageInfo.packageRoot,
packageVersion: '0.0.0-private',
private: true,
releaseMode: 'ci-artifact-only',
})),
{
ecosystem: 'python',
packageName: 'klo-sl',
packageRoot: 'python/klo-sl',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
{
ecosystem: 'python',
packageName: 'klo-daemon',
packageRoot: 'python/klo-daemon',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
]);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe('findPythonArtifacts', () => {
it('finds one wheel and one source distribution for each Python package', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-test-'));
try {
await writeFile(join(root, 'klo_sl-0.1.0-py3-none-any.whl'), '');
await writeFile(join(root, 'klo_sl-0.1.0.tar.gz'), '');
await writeFile(join(root, 'klo_daemon-0.1.0-py3-none-any.whl'), '');
await writeFile(join(root, 'klo_daemon-0.1.0.tar.gz'), '');
assert.deepEqual(await findPythonArtifacts(root), {
kloSlWheel: join(root, 'klo_sl-0.1.0-py3-none-any.whl'),
kloSlSdist: join(root, 'klo_sl-0.1.0.tar.gz'),
kloDaemonWheel: join(root, 'klo_daemon-0.1.0-py3-none-any.whl'),
kloDaemonSdist: join(root, 'klo_daemon-0.1.0.tar.gz'),
});
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('throws when a required Python artifact is missing', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-test-'));
try {
await assert.rejects(() => findPythonArtifacts(root), /Missing Python artifact: klo-sl wheel/);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe('artifact manifest', () => {
it('writes release metadata, source revision, checksums, and byte counts for every uploadable artifact', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-manifest-test-'));
const layout = packageArtifactLayout(root);
try {
await writeReleaseMetadataInputs(root);
await writeUploadableArtifactFixtures(layout);
const manifest = await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), {
sourceRevision: 'abc123',
});
assert.equal(artifactManifestPath(layout), join(root, 'dist', 'artifacts', 'manifest.json'));
assert.equal(manifest.schemaVersion, 2);
assert.equal(manifest.generatedAt, '2026-04-28T12:00:00.000Z');
assert.equal(manifest.sourceRevision, 'abc123');
assert.deepEqual(
manifest.packages.filter((entry) => entry.ecosystem === 'npm'),
NPM_ARTIFACT_PACKAGES.map((packageInfo) => ({
ecosystem: 'npm',
packageName: packageInfo.name,
packageRoot: packageInfo.packageRoot,
packageVersion: '0.0.0-private',
private: true,
releaseMode: 'ci-artifact-only',
})),
);
assert.deepEqual(
manifest.packages.filter((entry) => entry.ecosystem === 'python'),
[
{
ecosystem: 'python',
packageName: 'klo-sl',
packageRoot: 'python/klo-sl',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
{
ecosystem: 'python',
packageName: 'klo-daemon',
packageRoot: 'python/klo-daemon',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
],
);
assert.deepEqual(
manifest.files
.filter((file) => file.ecosystem === 'npm')
.map((file) => ({
artifactKind: file.artifactKind,
ecosystem: file.ecosystem,
packageName: file.packageName,
packageVersion: file.packageVersion,
path: file.path,
}))
.sort((left, right) => left.packageName.localeCompare(right.packageName)),
NPM_ARTIFACT_PACKAGES.map((packageInfo) => ({
artifactKind: 'tarball',
ecosystem: 'npm',
packageName: packageInfo.name,
packageVersion: '0.0.0-private',
path: expectedNpmArtifactPath(packageInfo.name),
})).sort((left, right) => left.packageName.localeCompare(right.packageName)),
);
assert.deepEqual(
manifest.files
.filter((file) => file.ecosystem === 'python')
.map((file) => ({
artifactKind: file.artifactKind,
ecosystem: file.ecosystem,
packageName: file.packageName,
packageVersion: file.packageVersion,
path: file.path,
})),
[
{
artifactKind: 'wheel',
ecosystem: 'python',
packageName: 'klo-daemon',
packageVersion: '0.1.0',
path: 'python/klo_daemon-0.1.0-py3-none-any.whl',
},
{
artifactKind: 'sdist',
ecosystem: 'python',
packageName: 'klo-daemon',
packageVersion: '0.1.0',
path: 'python/klo_daemon-0.1.0.tar.gz',
},
{
artifactKind: 'wheel',
ecosystem: 'python',
packageName: 'klo-sl',
packageVersion: '0.1.0',
path: 'python/klo_sl-0.1.0-py3-none-any.whl',
},
{
artifactKind: 'sdist',
ecosystem: 'python',
packageName: 'klo-sl',
packageVersion: '0.1.0',
path: 'python/klo_sl-0.1.0.tar.gz',
},
],
);
const sqliteEntry = manifest.files.find((file) => file.path === 'npm/klo-connector-sqlite-0.0.0-private.tgz');
assert.ok(sqliteEntry);
assert.equal(sqliteEntry.bytes, Buffer.byteLength('@klo/connector-sqlite-tarball'));
assert.equal(sqliteEntry.sha256, createHash('sha256').update('@klo/connector-sqlite-tarball').digest('hex'));
const writtenManifest = JSON.parse(await readFile(artifactManifestPath(layout), 'utf-8'));
assert.deepEqual(writtenManifest, manifest);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe('verifyArtifactManifest', () => {
it('accepts a schema version 2 manifest that matches the artifact directory', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-verify-manifest-test-'));
const layout = packageArtifactLayout(root);
try {
await writeReleaseMetadataInputs(root);
await writeUploadableArtifactFixtures(layout);
await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), {
sourceRevision: 'abc123',
});
const manifest = await verifyArtifactManifest(layout, {
expectedSourceRevision: 'abc123',
});
assert.equal(manifest.schemaVersion, 2);
assert.equal(manifest.sourceRevision, 'abc123');
assert.equal(manifest.files.length, NPM_ARTIFACT_PACKAGES.length + 4);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects a manifest when a file checksum has drifted', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-checksum-drift-test-'));
const layout = packageArtifactLayout(root);
try {
await writeReleaseMetadataInputs(root);
await writeUploadableArtifactFixtures(layout);
await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), {
sourceRevision: 'abc123',
});
await writeFile(layout.contextTarball, 'changed-context-tarball');
await assert.rejects(
() => verifyArtifactManifest(layout),
/Artifact manifest files do not match artifact contents/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects a manifest with an unsafe artifact path', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-path-test-'));
const layout = packageArtifactLayout(root);
try {
await writeReleaseMetadataInputs(root);
await writeUploadableArtifactFixtures(layout);
const manifest = await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), {
sourceRevision: 'abc123',
});
manifest.files[0].path = '../outside.tgz';
await writeFile(artifactManifestPath(layout), `${JSON.stringify(manifest, null, 2)}\n`);
await assert.rejects(() => verifyArtifactManifest(layout), /Unsafe artifact manifest path: \.\.\/outside\.tgz/);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects a manifest from the wrong source revision when one is required', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-revision-test-'));
const layout = packageArtifactLayout(root);
try {
await writeReleaseMetadataInputs(root);
await writeUploadableArtifactFixtures(layout);
await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), {
sourceRevision: 'abc123',
});
await assert.rejects(
() =>
verifyArtifactManifest(layout, {
expectedSourceRevision: 'def456',
}),
/Artifact manifest sourceRevision mismatch: expected def456, got abc123/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe('pythonArtifactInstallArgs', () => {
it('installs the built Python wheels by artifact path', () => {
const args = pythonArtifactInstallArgs('/tmp/smoke/.venv/bin/python', {
kloSlWheel: '/repo/klo/dist/artifacts/python/klo_sl-0.1.0-py3-none-any.whl',
kloSlSdist: '/repo/klo/dist/artifacts/python/klo_sl-0.1.0.tar.gz',
kloDaemonWheel: '/repo/klo/dist/artifacts/python/klo_daemon-0.1.0-py3-none-any.whl',
kloDaemonSdist: '/repo/klo/dist/artifacts/python/klo_daemon-0.1.0.tar.gz',
});
assert.deepEqual(args, [
'pip',
'install',
'--python',
'/tmp/smoke/.venv/bin/python',
'/repo/klo/dist/artifacts/python/klo_sl-0.1.0-py3-none-any.whl',
'/repo/klo/dist/artifacts/python/klo_daemon-0.1.0-py3-none-any.whl',
]);
assert.equal(args.includes('klo-daemon'), false);
assert.equal(args.includes('--find-links'), false);
});
});
describe('npmSmokePythonEnv', () => {
it('prepends the npm smoke virtualenv bin directory to PATH', () => {
const env = npmSmokePythonEnv('/tmp/klo-npm-smoke', { PATH: '/usr/bin' });
assert.match(env.PATH, /^\/tmp\/klo-npm-smoke\/\.venv\/(bin|Scripts)/);
assert.match(env.PATH, /\/usr\/bin$/);
});
});
describe('verification snippets', () => {
it('pins smoke dependencies and connector packages to clean-install-safe artifacts', () => {
const layout = packageArtifactLayout('/repo/klo');
const packageJson = npmSmokePackageJson(layout);
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
assert.equal(packageJson.dependencies[packageInfo.name], `file:${layout.npmTarballs[packageInfo.name]}`);
assert.equal(packageJson.pnpm.overrides[packageInfo.name], `file:${layout.npmTarballs[packageInfo.name]}`);
}
assert.equal(packageJson.dependencies['@modelcontextprotocol/sdk'], '^1.27.1');
assert.deepEqual(packageJson.pnpm.onlyBuiltDependencies, ['better-sqlite3']);
});
it('exposes manifest verification as a package artifact command', async () => {
const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8');
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
assert.match(source, /if \(command === 'verify-manifest'\)/);
assert.match(source, /await verifyArtifactManifest\(layout\)/);
assert.equal(packageJson.scripts['artifacts:verify-demo'], 'node scripts/package-artifacts.mjs verify-demo');
assert.equal(packageJson.scripts['artifacts:verify-manifest'], 'node scripts/package-artifacts.mjs verify-manifest');
});
it('verifies installed dbt extraction exports from @klo/context/ingest', () => {
const source = npmVerifySource();
assert.match(source, /const ingest = await import\('@klo\/context\/ingest'\);/);
assert.match(source, /const dbtExtractionExports = \[/);
assert.match(source, /throw new Error\('Missing dbt extraction export: ' \+ exportName\);/);
for (const exportName of [
'parseMetricflowFiles',
'parseMetricflowPullConfig',
'importMetricflowSemanticModels',
'parseDbtSchemaFiles',
'toDescriptionUpdates',
'toRelationshipUpdates',
'mergeSemanticModelTables',
'loadProjectInfo',
'loadDbtSchemaFiles',
]) {
assert.match(source, new RegExp(`\\['${exportName}', ingest\\.${exportName}\\]`));
}
});
it('asserts the public npm and connector entry points that clean installs must expose', () => {
const source = npmVerifySource();
assert.match(source, /@klo\/context/);
assert.match(source, /@klo\/context\/project/);
assert.match(source, /@klo\/context\/mcp/);
assert.match(source, /@klo\/context\/memory/);
assert.match(source, /@klo\/context\/daemon/);
assert.match(source, /@klo\/cli/);
assert.match(source, /@klo\/llm/);
assert.match(source, /createKloLlmProvider/);
assert.match(source, /KloMessageBuilder/);
assert.match(source, /createKloEmbeddingProvider/);
assert.doesNotMatch(source, /createGatewayLlmProvider/);
assert.match(source, /createLocalProjectMemoryCapture/);
for (const packageName of CONNECTOR_PACKAGE_NAMES) {
assert.match(source, new RegExp(packageName.replace('/', '\\/')));
}
assert.match(source, /KloSqliteScanConnector/);
assert.match(source, /KloPostgresScanConnector/);
assert.match(source, /KloBigQueryScanConnector/);
assert.match(source, /KloSnowflakeScanConnector/);
assert.match(source, /KloPostHogScanConnector/);
});
it('asserts installed hybrid search exports and CLI smoke coverage', () => {
const verifySource = npmVerifySource();
const runtimeSource = npmRuntimeSmokeSource();
const demoSource = npmDemoSmokeSource();
assert.match(verifySource, /const search = await import\('@klo\/context\/search'\);/);
assert.match(verifySource, /HybridSearchCore/);
assert.match(verifySource, /assertSearchBackendConformanceCase/);
assert.match(verifySource, /assertSearchBackendCapabilities/);
assert.match(runtimeSource, /klo agent wiki search hybrid metadata verified/);
assert.match(runtimeSource, /klo agent sl list hybrid metadata verified/);
assert.match(runtimeSource, /agent_sl_search_missing_project/);
assert.match(runtimeSource, /agent_sl_search_no_connections/);
assert.match(runtimeSource, /agent_sl_search_no_indexed_sources/);
assert.match(demoSource, /klo seeded demo agent wiki search verified/);
assert.match(demoSource, /klo seeded demo agent sl search verified/);
});
it('runs installed CLI commands and MCP through an installed daemon HTTP server', () => {
const source = npmRuntimeSmokeSource();
assert.match(source, /@modelcontextprotocol\/sdk\/client\/index\.js/);
assert.match(source, /@modelcontextprotocol\/sdk\/client\/stdio\.js/);
assert.match(source, /spawn\(command, args/);
assert.match(source, /createServer/);
assert.match(source, /request as httpRequest/);
assert.match(source, /getAvailablePort/);
assert.match(source, /startSemanticDaemon/);
assert.match(source, /waitForHttpHealth/);
assert.match(source, /stopSemanticDaemon/);
assert.match(source, /'klo-daemon'/);
assert.match(source, /'serve-http'/);
assert.match(source, /'--host'/);
assert.match(source, /'127\.0\.0\.1'/);
assert.match(source, /'--port'/);
assert.match(source, /\/health/);
assert.match(source, /--semantic-compute-url/);
assert.match(source, /createDaemonLookerTableIdentifierParser/);
assert.match(source, /LocalLookerRuntimeStore/);
assert.match(source, /Looker daemon table identifier parser verified/);
assert.match(source, /Looker local runtime store verified/);
assert.match(source, /semanticComputeUrl/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'setup'/);
assert.match(source, /knowledge', 'global', 'revenue\.md'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'agent',\s*'wiki',\s*'search'/);
assert.match(source, /semantic-layer', 'warehouse', 'orders\.yaml'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'agent',\s*'sl',\s*'list'/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'agent',\s*'sl',\s*'query'/);
assert.match(source, /orders\.order_count/);
assert.match(source, /sqlite3/);
assert.match(source, /driver: sqlite/);
assert.match(source, /path: warehouse\.db/);
assert.match(source, /live-database/);
assert.match(source, /'--execute'/);
assert.match(source, /'--execute-queries'/);
assert.match(source, /slValidateResult\.success, true/);
assert.match(source, /slQueryResult\.dialect, 'sqlite'/);
assert.match(source, /slQueryResult\.plan\.execution\.driver, 'sqlite'/);
assert.match(source, /"mode": "compile_only"/);
assert.match(source, /"mode": "executed"/);
assert.match(source, /klo agent sl query sqlite execute/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'dev',\s*'scan',\s*'warehouse'/);
assert.match(source, /'--mode',\s*'enriched'/);
assert.doesNotMatch(source, /'--enrich'/);
assert.match(source, /klo scan structural verified/);
assert.match(source, /klo scan enriched verified/);
assert.match(source, /scanReportJson\.artifactPaths\.manifestShards/);
assert.match(source, /scanReportJson\.artifactPaths\.enrichmentArtifacts/);
assert.match(source, /enrichment:/);
assert.match(source, /mode: deterministic/);
assert.match(source, /backend: gateway/);
assert.match(source, /models:/);
assert.match(source, /default: smoke\/provider/);
assert.match(source, /api_key: env:AI_GATEWAY_API_KEY/);
assert.match(source, /run\('pnpm', \['exec', 'klo', 'dev', 'ingest', 'run'/);
assert.match(source, /'serve', '--mcp', 'stdio'/);
assert.doesNotMatch(source, /'--semantic-compute',\n\s*'--execute-queries'/);
assert.match(source, /'--memory-capture', '--memory-model', 'smoke\/provider'/);
assert.match(source, /mcpServerStderr/);
assert.match(source, /klo serve stderr/);
assert.match(source, /sl_validate/);
assert.match(source, /sl_query/);
assert.match(source, /memory_capture/);
assert.match(source, /memory_capture_status/);
assert.match(source, /connection_test/);
assert.match(source, /scan_trigger/);
assert.match(source, /scan_status/);
assert.match(source, /scan_report/);
assert.match(source, /scan_list_artifacts/);
assert.match(source, /scan_read_artifact/);
assert.match(source, /mcpScanArtifacts\.artifacts\.find/);
assert.match(source, /AI_GATEWAY_API_KEY/);
assert.match(source, /access\(join\(projectDir, '\.klo', 'db\.sqlite'\)\)/);
assert.match(source, /SQLite knowledge index/);
assert.match(source, /klo dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway/);
assert.match(source, /klo dev ingest provider guard verified/);
});
describe('npmDemoSmokeSource', () => {
it('exercises the public packed-demo first-run contract', () => {
const source = npmDemoSmokeSource();
assert.match(source, /pnpm', \['exec', 'klo', '--help'\]/);
assert.match(source, /'demo', '--project-dir', projectDir, '--no-input', '--plain'/);
assert.match(source, /Mode: seeded/);
assert.match(source, /Source: packaged demo project/);
assert.match(source, /LLM calls: none/);
assert.match(source, /klo serve --mcp stdio/);
assert.doesNotMatch(source, new RegExp(["'demo'", "'--mode'", "'deterministic'"].join(', ')));
assert.match(source, /'dev', 'doctor', 'setup', '--no-input'/);
assert.match(source, /'--plain'/);
assert.match(source, /klo setup demo seeded wrote unexpected stderr/);
});
});
it('checks packaged ingest runtime assets in the installed npm smoke', () => {
const source = npmRuntimeSmokeSource();
assert.match(source, /notion_synthesize\/SKILL\.md/);
assert.match(source, /skills\/page_triage_classifier\.md/);
assert.match(source, /skills\/light_extraction\.md/);
});
it('asserts the Python modules that clean installs must expose', () => {
const source = pythonVerifySource();
assert.match(source, /semantic_layer/);
assert.match(source, /klo_daemon/);
assert.match(source, /importlib.metadata/);
});
});

195
scripts/precommit-check.mjs Normal file
View file

@ -0,0 +1,195 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import { dirname, join, relative, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
const scriptPath = fileURLToPath(import.meta.url);
const kloRoot = dirname(dirname(scriptPath));
const repoRoot = dirname(kloRoot);
const packageNameByDir = new Map(
[
'cli',
'connector-bigquery',
'connector-clickhouse',
'connector-mysql',
'connector-postgres',
'connector-posthog',
'connector-snowflake',
'connector-sqlite',
'connector-sqlserver',
'context',
'llm',
].map((packageDir) => {
const manifestPath = join(kloRoot, 'packages', packageDir, 'package.json');
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
return [packageDir, manifest.name];
}),
);
const packageCodePattern = /\.(?:ts|tsx|js|jsx|json)$/;
const scriptPattern = /\.(?:mjs|js|json)$/;
const pythonPackageTests = new Map([
['klo-sl', 'python/klo-sl/tests'],
['klo-daemon', 'python/klo-daemon/tests'],
]);
function normalizeFilePath(filePath) {
return filePath.replaceAll('\\', '/').replace(/^\.\//, '');
}
function stablePush(commands, key, cmd, args) {
if (commands.some((command) => command.key === key)) {
return;
}
commands.push({ key, cmd, args });
}
function maybeScriptTest(scriptFile) {
if (scriptFile.endsWith('.test.mjs')) {
return scriptFile;
}
if (!scriptFile.endsWith('.mjs')) {
return null;
}
const testFile = scriptFile.replace(/\.mjs$/, '.test.mjs');
return existsSync(join(kloRoot, testFile)) ? testFile : null;
}
export function planChecks(files) {
const commands = [];
const packageNames = new Set();
const pythonPackages = new Set();
let runBoundaryCheck = false;
let runAllTypeChecks = false;
let runAllPythonTests = false;
for (const rawFile of files) {
const file = normalizeFilePath(rawFile);
if (!file.startsWith('klo/')) {
continue;
}
const kloFile = file.slice('klo/'.length);
if (kloFile.startsWith('packages/')) {
const [, packageDir, ...rest] = kloFile.split('/');
const packageName = packageNameByDir.get(packageDir);
const packageFile = rest.join('/');
if (packageName && packageCodePattern.test(packageFile)) {
packageNames.add(packageName);
runBoundaryCheck = true;
}
continue;
}
if (kloFile.startsWith('scripts/') && scriptPattern.test(kloFile)) {
const testFile = maybeScriptTest(kloFile);
if (testFile) {
stablePush(commands, `script-test:${testFile}`, 'node', ['--test', testFile]);
}
continue;
}
if (kloFile.startsWith('python/')) {
const [, packageDir] = kloFile.split('/');
if (pythonPackageTests.has(packageDir)) {
pythonPackages.add(packageDir);
}
continue;
}
if (
['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'release-policy.json', 'tsconfig.base.json'].includes(
kloFile,
)
) {
runBoundaryCheck = true;
runAllTypeChecks = true;
continue;
}
if (['pyproject.toml', 'uv.lock', 'uv.toml'].includes(kloFile)) {
runAllPythonTests = true;
}
}
if (runBoundaryCheck) {
stablePush(commands, 'boundary-check', 'node', ['scripts/check-boundaries.mjs']);
}
if (runAllTypeChecks) {
stablePush(commands, 'type-check:all', 'pnpm', ['--filter', './packages/*', 'run', 'type-check']);
} else {
for (const packageName of [...packageNames].sort()) {
stablePush(commands, `type-check:${packageName}`, 'pnpm', ['--filter', packageName, 'run', 'type-check']);
stablePush(commands, `build:${packageName}`, 'pnpm', ['--filter', `${packageName}...`, 'run', 'build']);
stablePush(commands, `test:${packageName}`, 'pnpm', ['--filter', packageName, 'run', 'test']);
}
}
if (runAllPythonTests) {
stablePush(commands, 'pytest:all', 'uv', ['run', 'pytest']);
} else {
for (const packageDir of [...pythonPackages].sort()) {
stablePush(commands, `pytest:${packageDir}`, 'uv', [
'run',
'--package',
packageDir,
'pytest',
pythonPackageTests.get(packageDir),
]);
}
}
return commands;
}
function printCommand(command) {
console.log(`\n$ ${command.cmd} ${command.args.join(' ')}`);
}
export function runChecks(files) {
const commands = planChecks(files);
if (commands.length === 0) {
console.log('No KLO package checks needed for these files.');
return 0;
}
for (const command of commands) {
printCommand(command);
const result = spawnSync(command.cmd, command.args, {
cwd: kloRoot,
stdio: 'inherit',
env: process.env,
});
if (result.error) {
console.error(result.error.message);
return 1;
}
if (result.status !== 0) {
return result.status ?? 1;
}
}
return 0;
}
if (process.argv[1] && relative(repoRoot, process.argv[1]).split(sep).join('/') === 'klo/scripts/precommit-check.mjs') {
process.exitCode = runChecks(process.argv.slice(2));
}

View file

@ -0,0 +1,33 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { planChecks } from './precommit-check.mjs';
function commandKeys(files) {
return planChecks(files).map((command) => command.key);
}
describe('precommit-check', () => {
it('skips files outside klo', () => {
assert.deepEqual(commandKeys(['server/src/app.ts']), []);
});
it('runs only the touched package checks for package code', () => {
assert.deepEqual(commandKeys(['klo/packages/cli/src/index.ts']), [
'boundary-check',
'type-check:@klo/cli',
'build:@klo/cli',
'test:@klo/cli',
]);
});
it('runs the matching script test when a script changes', () => {
assert.deepEqual(commandKeys(['klo/scripts/check-boundaries.mjs']), [
'script-test:scripts/check-boundaries.test.mjs',
]);
});
it('runs the touched python package tests', () => {
assert.deepEqual(commandKeys(['klo/python/klo-sl/semantic_layer/parser.py']), ['pytest:klo-sl']);
});
});

View file

@ -0,0 +1,44 @@
#!/usr/bin/env node
import { constants } from 'node:fs';
import { access, chmod } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
export function kloRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
export function cliBinPath(rootDir = kloRootDir()) {
return resolve(rootDir, 'packages', 'cli', 'dist', 'bin.js');
}
async function canExecute(path) {
try {
await access(path, constants.X_OK);
return true;
} catch {
return false;
}
}
export async function ensureCliBinExecutable(rootDir = kloRootDir()) {
const binPath = cliBinPath(rootDir);
await access(binPath, constants.R_OK);
if (process.platform !== 'win32' && !(await canExecute(binPath))) {
await chmod(binPath, 0o755);
}
return binPath;
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
const binPath = await ensureCliBinExecutable();
process.stdout.write(`Prepared KLO CLI bin: ${binPath}\n`);
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}
}

View file

@ -0,0 +1,36 @@
{
"fixtures": [
{
"id": "chinook_with_declared_metadata",
"displayName": "Chinook (SQLite, declared metadata)",
"url": "https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite",
"sha256": "7651ba378ac2fcd0dfc3c66fb101f7a7eed3ba39a612ec642b96e20702061f15",
"license": "MIT",
"source": "https://github.com/lerocha/chinook-database"
},
{
"id": "northwind_with_declared_metadata",
"displayName": "Northwind (SQLite, declared metadata)",
"url": "https://github.com/jpwhite3/northwind-SQLite3/raw/main/dist/northwind.db",
"sha256": "2f4f5c68dfcd33ba27373eae48c7a4869800c68095ee0f9f0da494f83382a877",
"license": "MIT",
"source": "https://github.com/jpwhite3/northwind-SQLite3"
},
{
"id": "sakila_with_declared_metadata",
"displayName": "Sakila (SQLite, declared metadata)",
"url": "https://raw.githubusercontent.com/bradleygrant/sakila-sqlite3/master/sakila_master.db",
"sha256": "88c91a4a1a6b61f9d3f35904c0a173c887b25e73f20c3c2fdb073818c06f4268",
"license": "BSD-2-Clause",
"source": "https://github.com/bradleygrant/sakila-sqlite3"
},
{
"id": "adventureworkslt_with_declared_metadata",
"displayName": "AdventureWorksLT (SQLite, declared metadata)",
"url": "https://github.com/nuitsjp/AdventureWorks-for-SQLite/releases/download/Release-1_0_0/AdventureWorksLT.db",
"sha256": "f1a87a31f4efb5654f57a3b1ca47fac338972ceb7553673d66ea0bd9d55a7008", "_allowlist": "// pragma: allowlist secret",
"license": "MIT",
"source": "https://github.com/nuitsjp/AdventureWorks-for-SQLite"
}
]
}

View file

@ -0,0 +1,152 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
export const DEFAULT_VERSION_TAG = 'latest';
export const NO_PACKAGE_REASON =
'Set KLO_PUBLISHED_KLO_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.';
function optionalTrimmedString(value) {
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
}
function assertSafePackageName(packageName, label) {
if (!/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(packageName)) {
throw new Error(`Invalid ${label}: ${packageName}`);
}
}
function assertSafeVersionTag(version, label) {
if (!/^[a-zA-Z0-9][a-zA-Z0-9._+-]*$/.test(version)) {
throw new Error(`Invalid ${label}: ${version}`);
}
}
function assertHttpRegistry(registry, label) {
const parsed = new URL(registry);
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
throw new Error(`${label} must be an http(s) URL`);
}
}
function normalizePolicyConfig(policyConfig = {}) {
if (policyConfig === null || policyConfig === undefined) {
return { packageName: null, version: DEFAULT_VERSION_TAG, registry: null };
}
if (typeof policyConfig !== 'object' || Array.isArray(policyConfig)) {
throw new Error('release-policy.json publishedPackageSmoke must be a JSON object');
}
const normalized = {
packageName: optionalTrimmedString(policyConfig.packageName),
version: optionalTrimmedString(policyConfig.version) ?? DEFAULT_VERSION_TAG,
registry: optionalTrimmedString(policyConfig.registry),
};
assertSafeVersionTag(normalized.version, 'release-policy.json publishedPackageSmoke.version');
if (normalized.registry) {
assertHttpRegistry(normalized.registry, 'release-policy.json publishedPackageSmoke.registry');
}
return normalized;
}
export function readPublishedPackageSmokeConfig(env = process.env, args = process.argv.slice(2), policyConfig = {}) {
const requireConfig = args.includes('--require-config');
const policy = normalizePolicyConfig(policyConfig);
const envPackageName = optionalTrimmedString(env.KLO_PUBLISHED_KLO_PACKAGE);
const packageName = envPackageName ?? policy.packageName;
if (!packageName) {
return {
enabled: false,
requireConfig,
reason: NO_PACKAGE_REASON,
};
}
const configSource = envPackageName ? 'environment' : 'release-policy';
assertSafePackageName(
packageName,
configSource === 'environment'
? 'KLO_PUBLISHED_KLO_PACKAGE'
: 'release-policy.json publishedPackageSmoke.packageName',
);
const packageVersion = optionalTrimmedString(env.KLO_PUBLISHED_KLO_VERSION) ?? policy.version;
assertSafeVersionTag(
packageVersion,
optionalTrimmedString(env.KLO_PUBLISHED_KLO_VERSION)
? 'KLO_PUBLISHED_KLO_VERSION'
: 'release-policy.json publishedPackageSmoke.version',
);
const registry = optionalTrimmedString(env.KLO_PUBLISHED_KLO_REGISTRY) ?? policy.registry;
if (registry) {
assertHttpRegistry(
registry,
optionalTrimmedString(env.KLO_PUBLISHED_KLO_REGISTRY)
? 'KLO_PUBLISHED_KLO_REGISTRY'
: 'release-policy.json publishedPackageSmoke.registry',
);
}
return {
enabled: true,
requireConfig,
configSource,
packageName,
packageVersion,
registry,
};
}
export async function readPublishedPackageSmokeConfigFromPolicyFile(
policyPath,
env = process.env,
args = process.argv.slice(2),
) {
const policy = JSON.parse(await readFile(policyPath, 'utf8'));
return readPublishedPackageSmokeConfig(env, args, policy.publishedPackageSmoke ?? {});
}
export function publishedPackageSpec(config) {
assert.equal(config.enabled, true, 'publishedPackageSpec requires an enabled smoke config');
return `${config.packageName}@${config.packageVersion}`;
}
export function buildPublishedPackageNpxCommand(config, args, label = 'published package command') {
const env = config.registry ? { npm_config_registry: config.registry } : {};
return {
label,
command: 'npx',
args: ['--yes', publishedPackageSpec(config), ...args],
env,
};
}
export function buildPublishedPackageSmokeCommands(config, projectDir, emptyProjectDir) {
return [
buildPublishedPackageNpxCommand(config, ['--version'], 'published package version'),
buildPublishedPackageNpxCommand(
config,
['demo', '--project-dir', projectDir, '--no-input', '--plain'],
'published package demo',
),
buildPublishedPackageNpxCommand(
config,
['agent', 'wiki', 'search', 'ARR contract', '--json', '--limit', '5', '--project-dir', projectDir],
'published package wiki hybrid search',
),
buildPublishedPackageNpxCommand(
config,
['agent', 'sl', 'list', '--json', '--query', 'ARR', '--project-dir', projectDir],
'published package semantic-layer hybrid search',
),
buildPublishedPackageNpxCommand(
config,
['agent', 'sl', 'list', '--json', '--query', 'revenue', '--project-dir', emptyProjectDir],
'published package missing-project readiness',
),
];
}

View file

@ -0,0 +1,164 @@
#!/usr/bin/env node
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { promisify } from 'node:util';
import {
buildPublishedPackageSmokeCommands,
readPublishedPackageSmokeConfigFromPolicyFile,
} from './published-package-smoke-config.mjs';
export {
buildPublishedPackageNpxCommand,
buildPublishedPackageSmokeCommands,
publishedPackageSpec,
readPublishedPackageSmokeConfig,
} from './published-package-smoke-config.mjs';
const execFileAsync = promisify(execFile);
const SMOKE_TIMEOUT_MS = 180_000;
function scriptRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
function releasePolicyPath(rootDir = scriptRootDir()) {
return join(rootDir, 'release-policy.json');
}
async function runCommand(command, args, options = {}) {
process.stdout.write(`$ ${command} ${args.join(' ')}\n`);
try {
const result = await execFileAsync(command, args, {
cwd: options.cwd,
env: Object.assign({}, process.env, options.env ?? {}),
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
timeout: SMOKE_TIMEOUT_MS,
});
return { code: 0, stdout: result.stdout, stderr: result.stderr };
} catch (error) {
return {
code: typeof error.code === 'number' ? error.code : 1,
stdout: error.stdout ?? '',
stderr: error.stderr ?? error.message,
};
}
}
function requireSuccess(label, result) {
assert.equal(
result.code,
0,
`${label} failed with code ${result.code}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
);
}
function parseJson(label, text) {
try {
return JSON.parse(text);
} catch (error) {
throw new Error(`${label} did not produce JSON: ${error instanceof Error ? error.message : String(error)}\n${text}`);
}
}
function assertHybridWikiSearch(result) {
const payload = parseJson('published package wiki search', result.stdout);
assert.ok(payload.totalFound > 0, 'published package wiki search should return results');
assert.ok(
payload.results.some((entry) => Array.isArray(entry.matchReasons) && entry.matchReasons.length > 0),
'published package wiki search should expose match reasons',
);
}
function assertHybridSlSearch(result) {
const payload = parseJson('published package semantic-layer search', result.stdout);
assert.ok(payload.totalSources > 0, 'published package semantic-layer search should return sources');
assert.ok(
payload.sources.some((entry) => Array.isArray(entry.matchReasons) && entry.matchReasons.length > 0),
'published package semantic-layer search should expose match reasons',
);
}
function assertMissingProjectReadiness(result, emptyProjectDir) {
assert.equal(result.code, 1, 'missing-project semantic-layer search should exit 1');
assert.equal(result.stdout, '', 'missing-project semantic-layer search should not write JSON errors to stdout');
const payload = parseJson('published package missing-project semantic-layer search', result.stderr);
assert.deepEqual(payload, {
ok: false,
error: {
code: 'agent_sl_search_missing_project',
message: `Semantic-layer search needs an initialized KLO project at ${emptyProjectDir}.`,
nextSteps: [
'klo demo',
`klo setup --project-dir ${emptyProjectDir}`,
'klo ingest <connection>',
`klo agent sl list --json --query "revenue" --project-dir ${emptyProjectDir}`,
],
},
});
}
export async function runPublishedPackageSmoke(config) {
const root = await mkdtemp(join(tmpdir(), 'klo-published-package-smoke-'));
try {
const projectDir = join(root, 'demo-project');
const emptyProjectDir = join(root, 'empty-project');
await mkdir(emptyProjectDir, { recursive: true });
const commands = buildPublishedPackageSmokeCommands(config, projectDir, emptyProjectDir);
for (const command of commands.slice(0, 4)) {
const result = await runCommand(command.command, command.args, { env: command.env });
requireSuccess(command.label, result);
if (command.label === 'published package wiki hybrid search') {
assertHybridWikiSearch(result);
}
if (command.label === 'published package semantic-layer hybrid search') {
assertHybridSlSearch(result);
}
}
const missingProjectCommand = commands[4];
const missingProject = await runCommand(missingProjectCommand.command, missingProjectCommand.args, {
env: missingProjectCommand.env,
});
assertMissingProjectReadiness(missingProject, emptyProjectDir);
process.stdout.write('published package hybrid search smoke verified\n');
} finally {
await rm(root, { recursive: true, force: true });
}
}
async function main() {
const config = await readPublishedPackageSmokeConfigFromPolicyFile(
releasePolicyPath(),
process.env,
process.argv.slice(2),
);
if (!config.enabled) {
if (config.requireConfig) {
throw new Error(config.reason);
}
process.stdout.write(`Published KLO package smoke skipped: ${config.reason}\n`);
return;
}
await runPublishedPackageSmoke(config);
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
try {
await main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\n`);
process.exitCode = 1;
}
}

View file

@ -0,0 +1,256 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { describe, it } from 'node:test';
import {
buildPublishedPackageNpxCommand,
buildPublishedPackageSmokeCommands,
publishedPackageSpec,
readPublishedPackageSmokeConfig,
} from './published-package-smoke.mjs';
describe('published package smoke config', () => {
it('skips by default until a published package name is supplied', () => {
assert.deepEqual(readPublishedPackageSmokeConfig({}, []), {
enabled: false,
requireConfig: false,
reason:
'Set KLO_PUBLISHED_KLO_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.',
});
});
it('can require the published package config for post-publication CI', () => {
assert.deepEqual(readPublishedPackageSmokeConfig({}, ['--require-config']), {
enabled: false,
requireConfig: true,
reason:
'Set KLO_PUBLISHED_KLO_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.',
});
});
it('reads the package, version, and registry from environment variables', () => {
assert.deepEqual(
readPublishedPackageSmokeConfig(
{
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-public',
KLO_PUBLISHED_KLO_VERSION: 'latest',
KLO_PUBLISHED_KLO_REGISTRY: 'https://registry.npmjs.org/',
},
[],
),
{
enabled: true,
requireConfig: false,
configSource: 'environment',
packageName: '@klo/cli-public',
packageVersion: 'latest',
registry: 'https://registry.npmjs.org/',
},
);
});
it('reads the package, version, and registry from release policy when env vars are absent', () => {
assert.deepEqual(
readPublishedPackageSmokeConfig(
{},
[],
{
packageName: '@klo/cli-public',
version: '2026.5.8',
registry: 'https://registry.npmjs.org/',
},
),
{
enabled: true,
requireConfig: false,
configSource: 'release-policy',
packageName: '@klo/cli-public',
packageVersion: '2026.5.8',
registry: 'https://registry.npmjs.org/',
},
);
});
it('lets environment variables override release policy values', () => {
assert.deepEqual(
readPublishedPackageSmokeConfig(
{
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-from-env',
KLO_PUBLISHED_KLO_VERSION: 'latest',
},
[],
{
packageName: '@klo/cli-from-policy',
version: '2026.5.8',
registry: 'https://registry.npmjs.org/',
},
),
{
enabled: true,
requireConfig: false,
configSource: 'environment',
packageName: '@klo/cli-from-env',
packageVersion: 'latest',
registry: 'https://registry.npmjs.org/',
},
);
});
it('rejects package names that would be unsafe as npx package specs', () => {
assert.throws(
() => readPublishedPackageSmokeConfig({ KLO_PUBLISHED_KLO_PACKAGE: '--package=@evil/pkg' }, []),
/Invalid KLO_PUBLISHED_KLO_PACKAGE/,
);
assert.throws(
() => readPublishedPackageSmokeConfig({ KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli public' }, []),
/Invalid KLO_PUBLISHED_KLO_PACKAGE/,
);
assert.throws(
() =>
readPublishedPackageSmokeConfig(
{},
[],
{
packageName: '@klo/cli public',
version: 'latest',
registry: null,
},
),
/Invalid release-policy\.json publishedPackageSmoke\.packageName/,
);
});
it('rejects unsafe version tags and non-HTTP registries', () => {
assert.throws(
() =>
readPublishedPackageSmokeConfig(
{
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-public',
KLO_PUBLISHED_KLO_VERSION: '--tag latest',
},
[],
),
/Invalid KLO_PUBLISHED_KLO_VERSION/,
);
assert.throws(
() =>
readPublishedPackageSmokeConfig(
{
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-public',
KLO_PUBLISHED_KLO_REGISTRY: 'file:///tmp/npm',
},
[],
),
/KLO_PUBLISHED_KLO_REGISTRY must be an http\(s\) URL/,
);
});
});
describe('published package smoke command construction', () => {
const config = {
enabled: true,
requireConfig: false,
packageName: '@klo/cli-public',
packageVersion: 'latest',
registry: 'https://registry.npmjs.org/',
};
it('builds the npx package spec from package name and version tag', () => {
assert.equal(publishedPackageSpec(config), '@klo/cli-public@latest');
});
it('builds npx commands with a registry env patch instead of shell interpolation', () => {
assert.deepEqual(buildPublishedPackageNpxCommand(config, ['--version']), {
label: 'published package command',
command: 'npx',
args: ['--yes', '@klo/cli-public@latest', '--version'],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
});
});
it('builds the full hybrid-search smoke command list', () => {
assert.deepEqual(buildPublishedPackageSmokeCommands(config, '/tmp/klo-smoke/demo', '/tmp/klo-smoke/empty'), [
{
label: 'published package version',
command: 'npx',
args: ['--yes', '@klo/cli-public@latest', '--version'],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package demo',
command: 'npx',
args: [
'--yes',
'@klo/cli-public@latest',
'demo',
'--project-dir',
'/tmp/klo-smoke/demo',
'--no-input',
'--plain',
],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package wiki hybrid search',
command: 'npx',
args: [
'--yes',
'@klo/cli-public@latest',
'agent',
'wiki',
'search',
'ARR contract',
'--json',
'--limit',
'5',
'--project-dir',
'/tmp/klo-smoke/demo',
],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package semantic-layer hybrid search',
command: 'npx',
args: [
'--yes',
'@klo/cli-public@latest',
'agent',
'sl',
'list',
'--json',
'--query',
'ARR',
'--project-dir',
'/tmp/klo-smoke/demo',
],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package missing-project readiness',
command: 'npx',
args: [
'--yes',
'@klo/cli-public@latest',
'agent',
'sl',
'list',
'--json',
'--query',
'revenue',
'--project-dir',
'/tmp/klo-smoke/empty',
],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
]);
});
it('exposes the smoke through the package release script', async () => {
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
assert.equal(
packageJson.scripts['release:published-smoke'],
'node scripts/published-package-smoke.mjs --require-config',
);
});
});

View file

@ -0,0 +1,330 @@
#!/usr/bin/env node
import { mkdir as fsMkdir, writeFile as fsWriteFile } from 'node:fs/promises';
import { execFile as childExecFile } from 'node:child_process';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { promisify } from 'node:util';
import { runWorkspaceKlo } from './run-klo.mjs';
const scriptDir = dirname(fileURLToPath(import.meta.url));
const kloRootDir = resolve(scriptDir, '..');
const repoRootDir = resolve(kloRootDir, '..');
const defaultProjectDir = resolve(kloRootDir, 'examples/orbit-relationship-verification');
const defaultReportPath = resolve(
kloRootDir,
'examples/orbit-relationship-verification/reports/orbit-verification.md',
);
const defaultExecFile = promisify(childExecFile);
class BufferWriter {
chunks = [];
write(chunk) {
this.chunks.push(String(chunk));
}
text() {
return this.chunks.join('');
}
}
function dateOnly(date) {
return date.toISOString().slice(0, 10);
}
function trimForReport(value) {
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : 'none';
}
export function defaultOrbitVerificationProjectDir() {
return defaultProjectDir;
}
function shellCommand(argv) {
return ['pnpm', 'run', 'klo', '--', ...argv].join(' ');
}
function firstNonEmptyLine(...values) {
for (const value of values) {
const line = value
.split('\n')
.map((candidate) => candidate.trim())
.find((candidate) => candidate.length > 0);
if (line) {
return line;
}
}
return 'Orbit scan command failed before producing diagnostic output';
}
function parseArgs(argv) {
const options = {
connectionId: process.env.KLO_ORBIT_CONNECTION_ID ?? 'orbit',
projectDir: process.env.KLO_ORBIT_PROJECT_DIR ?? defaultProjectDir,
reportPath: defaultReportPath,
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === '--connection-id' || arg === '--connection') {
options.connectionId = argv[index + 1];
index += 1;
continue;
}
if (arg === '--project-dir') {
options.projectDir = argv[index + 1];
index += 1;
continue;
}
if (arg === '--report-path') {
options.reportPath = argv[index + 1];
index += 1;
continue;
}
throw new Error(`Unknown option: ${arg}`);
}
return options;
}
export function buildOrbitScanArgv(input) {
return ['dev', 'scan', input.connectionId, '--enrich', '--project-dir', input.projectDir];
}
export function buildOrbitReportArgv(input) {
return ['dev', 'scan', 'report', '--json', '--project-dir', input.projectDir, input.runId];
}
export function extractRunId(stdout) {
const match = stdout.match(/^Run:\s*(\S+)/m);
return match?.[1] ?? null;
}
function listLines(values) {
if (!values || values.length === 0) {
return ['- none'];
}
return values.map((value) => `- \`${value}\``);
}
function warningLines(report) {
if (!Array.isArray(report.warnings) || report.warnings.length === 0) {
return ['- none'];
}
return report.warnings.map((warning) => `- \`${warning.code}\`: ${warning.message}`);
}
function formatSuccess(result) {
const relationships = result.report.relationships ?? { accepted: 0, review: 0, rejected: 0, skipped: 0 };
const enrichment = result.report.enrichment ?? {};
const artifactPaths = result.report.artifactPaths ?? {};
return [
'## Outcome',
'',
'- Exit code: 0',
`- Run: \`${result.report.runId ?? 'unknown'}\``,
`- Connection: \`${result.report.connectionId ?? result.connectionId}\``,
`- Mode: \`${result.report.mode ?? 'unknown'}\``,
`- Sync: \`${result.report.syncId ?? 'unknown'}\``,
'',
'## Relationship Summary',
'',
`- Accepted: ${relationships.accepted ?? 0}`,
`- Review: ${relationships.review ?? 0}`,
`- Rejected: ${relationships.rejected ?? 0}`,
`- Skipped: ${relationships.skipped ?? 0}`,
'',
'## Enrichment Summary',
'',
`- Deterministic relationships: \`${enrichment.deterministicRelationships ?? 'unknown'}\``,
`- Statistical validation: \`${enrichment.statisticalValidation ?? 'unknown'}\``,
`- LLM relationship validation: \`${enrichment.llmRelationshipValidation ?? 'unknown'}\``,
'',
'## Artifacts',
'',
`- Report: \`${artifactPaths.reportPath ?? 'none'}\``,
`- Raw sources: \`${artifactPaths.rawSourcesDir ?? 'none'}\``,
'',
'Manifest shards:',
'',
...listLines(artifactPaths.manifestShards),
'',
'Enrichment artifacts:',
'',
...listLines(artifactPaths.enrichmentArtifacts),
'',
'Warnings:',
'',
...warningLines(result.report),
];
}
function formatBlocked(result) {
return [
'## Outcome',
'',
`- Exit code: ${result.scanExitCode}`,
`- Blocker: \`${result.blocker}\``,
'',
'## Evidence',
'',
'- Orbit verification was not executed because the current local Orbit scan command failed.',
'- Re-run with `--report-path` to write verification evidence to a custom location.',
'',
'Scan stdout:',
'',
'```text',
trimForReport(result.scanStdout),
'```',
'',
'Scan stderr:',
'',
'```text',
trimForReport(result.scanStderr),
'```',
];
}
export function formatOrbitVerificationMarkdown(result) {
const lines = [
'# KLO Relationship Discovery Orbit Verification',
'',
`Date: ${result.date}`,
'',
'## Command',
'',
'```bash',
result.scanCommand,
'```',
'',
];
if (result.status === 'success') {
lines.push(
'## JSON Report Command',
'',
'```bash',
result.reportCommand,
'```',
'',
...formatSuccess(result),
);
} else {
lines.push(...formatBlocked(result));
}
return `${lines.join('\n')}\n`;
}
async function runBufferedWorkspaceKlo(runner, argv, rootDir, execFile) {
const stdout = new BufferWriter();
const stderr = new BufferWriter();
const exitCode = await runner(argv, { rootDir, execFile, stdout, stderr });
return {
exitCode,
stdout: stdout.text(),
stderr: stderr.text(),
};
}
function orbitVerificationEnv(projectDir) {
if (projectDir !== defaultProjectDir) {
return process.env;
}
return {
...process.env,
GIT_CEILING_DIRECTORIES: dirname(defaultProjectDir),
};
}
export async function runOrbitVerification(options = {}) {
const connectionId = options.connectionId ?? process.env.KLO_ORBIT_CONNECTION_ID ?? 'orbit';
const projectDir = options.projectDir ?? process.env.KLO_ORBIT_PROJECT_DIR ?? defaultProjectDir;
const reportPath = options.reportPath ?? defaultReportPath;
const rootDir = options.rootDir ?? kloRootDir;
const runner = options.runWorkspaceKlo ?? runWorkspaceKlo;
const execFile = options.execFile ?? defaultExecFile;
const now = options.now ?? (() => new Date());
const mkdir = options.mkdir ?? fsMkdir;
const writeFile = options.writeFile ?? fsWriteFile;
const date = dateOnly(now());
const env = options.env ?? orbitVerificationEnv(projectDir);
const runWithEnv = (argv, runnerOptions) => runner(argv, { ...runnerOptions, env });
const scanArgv = buildOrbitScanArgv({ connectionId, projectDir });
const scan = await runBufferedWorkspaceKlo(runWithEnv, scanArgv, rootDir, execFile);
let result;
if (scan.exitCode !== 0) {
result = {
status: 'blocked',
date,
connectionId,
projectDir,
scanCommand: shellCommand(scanArgv),
scanExitCode: scan.exitCode,
blocker: firstNonEmptyLine(scan.stderr, scan.stdout),
scanStdout: scan.stdout,
scanStderr: scan.stderr,
};
} else {
const runId = extractRunId(scan.stdout);
if (!runId) {
result = {
status: 'blocked',
date,
connectionId,
projectDir,
scanCommand: shellCommand(scanArgv),
scanExitCode: scan.exitCode,
blocker: 'KLO scan completed without printing a Run id',
scanStdout: scan.stdout,
scanStderr: scan.stderr,
};
} else {
const reportArgv = buildOrbitReportArgv({ projectDir, runId });
const reportOutput = await runBufferedWorkspaceKlo(runWithEnv, reportArgv, rootDir, execFile);
if (reportOutput.exitCode !== 0) {
result = {
status: 'blocked',
date,
connectionId,
projectDir,
scanCommand: shellCommand(scanArgv),
scanExitCode: reportOutput.exitCode,
blocker: firstNonEmptyLine(reportOutput.stderr, reportOutput.stdout),
scanStdout: `${scan.stdout}\n${reportOutput.stdout}`.trim(),
scanStderr: `${scan.stderr}\n${reportOutput.stderr}`.trim(),
};
} else {
result = {
status: 'success',
date,
connectionId,
projectDir,
scanCommand: shellCommand(scanArgv),
reportCommand: shellCommand(reportArgv),
scanExitCode: scan.exitCode,
reportExitCode: reportOutput.exitCode,
scanStdout: scan.stdout,
scanStderr: scan.stderr,
report: JSON.parse(reportOutput.stdout),
};
}
}
}
await mkdir(dirname(reportPath), { recursive: true });
await writeFile(reportPath, formatOrbitVerificationMarkdown(result));
return result;
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
const options = parseArgs(process.argv.slice(2));
const result = await runOrbitVerification(options);
process.stdout.write(`Wrote ${options.reportPath}\n`);
process.stdout.write(`Outcome: ${result.status}\n`);
}

View file

@ -0,0 +1,244 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { describe, it } from 'node:test';
import {
buildOrbitReportArgv,
buildOrbitScanArgv,
defaultOrbitVerificationProjectDir,
extractRunId,
formatOrbitVerificationMarkdown,
runOrbitVerification,
} from './relationship-orbit-verification.mjs';
function successReportJson() {
return JSON.stringify({
runId: 'scan-orbit-1',
connectionId: 'orbit',
mode: 'enriched',
syncId: '2026-05-07-100000-scan-enriched-1',
relationships: {
accepted: 14,
review: 8,
rejected: 91,
skipped: 0,
},
enrichment: {
deterministicRelationships: 'completed',
statisticalValidation: 'completed',
llmRelationshipValidation: 'skipped',
},
warnings: [
{
code: 'scan_enrichment_backend_not_configured',
message:
'Skipping description and embedding enrichment because scan.enrichment.mode is not configured; relationship discovery still ran.',
recoverable: true,
},
],
artifactPaths: {
reportPath: 'raw-sources/orbit/live-database/2026-05-07-100000-scan-enriched-1/reports/scan-report.json',
rawSourcesDir: 'raw-sources/orbit/live-database/2026-05-07-100000-scan-enriched-1',
manifestShards: ['semantic-layer/orbit/_schema/orbit_analytics.yaml'],
enrichmentArtifacts: [
'raw-sources/orbit/live-database/2026-05-07-100000-scan-enriched-1/enrichment/relationships.json',
'raw-sources/orbit/live-database/2026-05-07-100000-scan-enriched-1/enrichment/relationship-profile.json',
'raw-sources/orbit/live-database/2026-05-07-100000-scan-enriched-1/enrichment/relationship-diagnostics.json',
],
},
});
}
describe('relationship Orbit verification helper', () => {
it('exposes the Orbit verification command from the KLO workspace package', async () => {
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
assert.equal(
packageJson.scripts['relationships:verify-orbit'],
'node scripts/relationship-orbit-verification.mjs',
);
});
it('builds the current KLO launcher arguments for scan and JSON report commands', () => {
assert.deepEqual(buildOrbitScanArgv({ connectionId: 'orbit', projectDir: '/tmp/orbit-project' }), [
'dev',
'scan',
'orbit',
'--enrich',
'--project-dir',
'/tmp/orbit-project',
]);
assert.deepEqual(buildOrbitReportArgv({ projectDir: '/tmp/orbit-project', runId: 'scan-orbit-1' }), [
'dev',
'scan',
'report',
'--json',
'--project-dir',
'/tmp/orbit-project',
'scan-orbit-1',
]);
});
it('uses the checked-in Orbit verification project by default', async () => {
const calls = [];
const envs = [];
const writes = [];
const defaultProjectDir = defaultOrbitVerificationProjectDir();
const result = await runOrbitVerification({
reportPath: '/tmp/orbit-report.md',
now: () => new Date('2026-05-07T10:00:00.000Z'),
mkdir: async () => {},
writeFile: async (path, content) => {
writes.push({ path, content });
},
runWorkspaceKlo: async (argv, options) => {
calls.push(argv);
envs.push(options.env);
if (argv[2] === 'report') {
options.stdout.write(successReportJson());
return 0;
}
options.stdout.write('KLO scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
return 0;
},
});
assert.equal(result.status, 'success');
assert.deepEqual(calls, [
['dev', 'scan', 'orbit', '--enrich', '--project-dir', defaultProjectDir],
['dev', 'scan', 'report', '--json', '--project-dir', defaultProjectDir, 'scan-orbit-1'],
]);
assert.equal(envs[0].GIT_CEILING_DIRECTORIES, dirname(defaultProjectDir));
assert.equal(envs[1].GIT_CEILING_DIRECTORIES, dirname(defaultProjectDir));
assert.equal(writes.length, 1);
assert.match(writes[0].content, new RegExp(defaultProjectDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
});
it('extracts the run id from human scan output', () => {
assert.equal(extractRunId(`KLO scan completed\nStatus: done\nRun: scan-orbit-1\nConnection: orbit\n`), 'scan-orbit-1');
assert.equal(extractRunId('KLO scan completed without a run line\n'), null);
});
it('formats successful Orbit verification evidence from the JSON report', () => {
const markdown = formatOrbitVerificationMarkdown({
status: 'success',
date: '2026-05-07',
connectionId: 'orbit',
projectDir: '/tmp/orbit-project',
scanCommand: 'pnpm run klo -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
reportCommand: 'pnpm run klo -- dev scan report --json --project-dir /tmp/orbit-project scan-orbit-1',
scanExitCode: 0,
reportExitCode: 0,
scanStdout: 'KLO scan completed\nRun: scan-orbit-1\n',
scanStderr: '',
report: JSON.parse(successReportJson()),
});
assert.match(markdown, /# KLO Relationship Discovery Orbit Verification/);
assert.match(markdown, /Outcome/);
assert.match(markdown, /Exit code: 0/);
assert.match(markdown, /Accepted: 14/);
assert.match(markdown, /Review: 8/);
assert.match(markdown, /Rejected: 91/);
assert.match(markdown, /semantic-layer\/orbit\/_schema\/orbit_analytics\.yaml/);
assert.match(markdown, /relationship-diagnostics\.json/);
assert.match(markdown, /scan_enrichment_backend_not_configured/);
});
it('formats blocked Orbit verification evidence from the current failing command', () => {
const markdown = formatOrbitVerificationMarkdown({
status: 'blocked',
date: '2026-05-07',
connectionId: 'orbit',
projectDir: '/tmp/orbit-project',
scanCommand: 'pnpm run klo -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
scanExitCode: 1,
blocker: 'Connection "orbit" was not found',
scanStdout: '',
scanStderr: 'Connection "orbit" was not found\n',
});
assert.match(markdown, /Exit code: 1/);
assert.match(markdown, /Connection "orbit" was not found/);
assert.match(markdown, /Orbit verification was not executed because the current local Orbit scan command failed/);
assert.doesNotMatch(markdown, /scan\.enrichment\.mode is required/);
});
it('runs scan then JSON report and writes success Markdown', async () => {
const calls = [];
const writes = [];
const result = await runOrbitVerification({
connectionId: 'orbit',
projectDir: '/tmp/orbit-project',
reportPath: '/tmp/orbit-report.md',
now: () => new Date('2026-05-07T10:00:00.000Z'),
mkdir: async () => {},
writeFile: async (path, content) => {
writes.push({ path, content });
},
runWorkspaceKlo: async (argv, options) => {
calls.push(argv);
if (argv[2] === 'report') {
options.stdout.write(successReportJson());
return 0;
}
options.stdout.write('KLO scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
return 0;
},
});
assert.equal(result.status, 'success');
assert.deepEqual(calls, [
['dev', 'scan', 'orbit', '--enrich', '--project-dir', '/tmp/orbit-project'],
['dev', 'scan', 'report', '--json', '--project-dir', '/tmp/orbit-project', 'scan-orbit-1'],
]);
assert.equal(writes.length, 1);
assert.equal(writes[0].path, '/tmp/orbit-report.md');
assert.match(writes[0].content, /Accepted: 14/);
});
it('writes blocked Markdown when the scan command fails before a run id exists', async () => {
const writes = [];
const result = await runOrbitVerification({
connectionId: 'orbit',
projectDir: '/tmp/orbit-project',
reportPath: '/tmp/orbit-report.md',
now: () => new Date('2026-05-07T10:00:00.000Z'),
mkdir: async () => {},
writeFile: async (path, content) => {
writes.push({ path, content });
},
runWorkspaceKlo: async (_argv, options) => {
options.stderr.write('Connection "orbit" was not found\n');
return 1;
},
});
assert.equal(result.status, 'blocked');
assert.equal(result.scanExitCode, 1);
assert.equal(writes.length, 1);
assert.match(writes[0].content, /Connection "orbit" was not found/);
});
it('runs the workspace launcher in buffered mode so real scan errors are captured', async () => {
let sawExecFile = false;
const result = await runOrbitVerification({
connectionId: 'orbit',
projectDir: '/tmp/orbit-project',
reportPath: '/tmp/orbit-report.md',
now: () => new Date('2026-05-07T10:00:00.000Z'),
mkdir: async () => {},
writeFile: async () => {},
execFile: async () => ({ stdout: '', stderr: '' }),
runWorkspaceKlo: async (_argv, options) => {
sawExecFile = typeof options.execFile === 'function';
options.stderr.write('ENOENT: no such file or directory, open \'/tmp/orbit-project/klo.yaml\'\n');
return 1;
},
});
assert.equal(sawExecFile, true);
assert.equal(result.blocker, "ENOENT: no such file or directory, open '/tmp/orbit-project/klo.yaml'");
});
});

View file

@ -0,0 +1,246 @@
#!/usr/bin/env node
import { readFile } from 'node:fs/promises';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { packageArtifactLayout, packageReleaseMetadata, verifyArtifactManifest } from './package-artifacts.mjs';
import { readPublishedPackageSmokeConfig } from './published-package-smoke-config.mjs';
function scriptRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
export function releasePolicyPath(rootDir = scriptRootDir()) {
return join(rootDir, 'release-policy.json');
}
async function readJson(path) {
return JSON.parse(await readFile(path, 'utf-8'));
}
const CI_ARTIFACT_ONLY_RELEASE_MODE = 'ci-artifact-only';
const PUBLISHED_PACKAGE_SMOKE_REQUIRED_RELEASE_MODE = 'published-package-smoke-required';
const SUPPORTED_RELEASE_MODES = new Set([
CI_ARTIFACT_ONLY_RELEASE_MODE,
PUBLISHED_PACKAGE_SMOKE_REQUIRED_RELEASE_MODE,
]);
export async function readReleasePolicy(rootDir = scriptRootDir()) {
return readJson(releasePolicyPath(rootDir));
}
function isPlainObject(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function assertPlainObject(value, label) {
if (!isPlainObject(value)) {
throw new Error(`${label} must be a JSON object`);
}
}
function assertBoolean(value, label) {
if (typeof value !== 'boolean') {
throw new Error(`${label} must be a boolean`);
}
}
function assertString(value, label) {
if (typeof value !== 'string') {
throw new Error(`${label} must be a string`);
}
}
function assertNullableString(value, label) {
if (value !== null && typeof value !== 'string') {
throw new Error(`${label} must be a string or null`);
}
}
function assertStringArray(value, label) {
if (!Array.isArray(value) || !value.every((entry) => typeof entry === 'string')) {
throw new Error(`${label} must be an array of strings`);
}
}
function assertSupportedReleaseMode(releaseMode) {
assertString(releaseMode, 'Release policy releaseMode');
if (!SUPPORTED_RELEASE_MODES.has(releaseMode)) {
throw new Error(`Unsupported release policy releaseMode: ${releaseMode}`);
}
}
function assertRequiredBeforePublishing(policy) {
assertStringArray(policy.requiredBeforePublishing, 'Release policy requiredBeforePublishing');
if (policy.releaseMode === CI_ARTIFACT_ONLY_RELEASE_MODE && policy.requiredBeforePublishing.length === 0) {
throw new Error('Release policy requiredBeforePublishing must list the remaining publishing decisions');
}
if (
policy.releaseMode === PUBLISHED_PACKAGE_SMOKE_REQUIRED_RELEASE_MODE &&
policy.requiredBeforePublishing.length > 0
) {
throw new Error('published-package-smoke-required release mode requires requiredBeforePublishing to be empty');
}
}
function assertSameMembers(actual, expected, label) {
const sortedActual = [...actual].sort();
const sortedExpected = [...expected].sort();
if (JSON.stringify(sortedActual) !== JSON.stringify(sortedExpected)) {
throw new Error(`${label} mismatch: expected ${sortedExpected.join(', ')}, got ${sortedActual.join(', ')}`);
}
}
export function validateReleasePolicy(policy) {
assertPlainObject(policy, 'Release policy');
if (policy.schemaVersion !== 1) {
throw new Error(`Unsupported release policy schemaVersion: ${policy.schemaVersion}`);
}
assertSupportedReleaseMode(policy.releaseMode);
assertPlainObject(policy.npm, 'Release policy npm');
assertPlainObject(policy.python, 'Release policy python');
assertPlainObject(policy.publishedPackageSmoke, 'Release policy publishedPackageSmoke');
assertBoolean(policy.npm.publish, 'Release policy npm.publish');
assertNullableString(policy.npm.registry, 'Release policy npm.registry');
assertStringArray(policy.npm.packages, 'Release policy npm.packages');
assertBoolean(policy.python.publish, 'Release policy python.publish');
assertNullableString(policy.python.repository, 'Release policy python.repository');
assertStringArray(policy.python.packages, 'Release policy python.packages');
assertNullableString(policy.publishedPackageSmoke.packageName, 'Release policy publishedPackageSmoke.packageName');
assertString(policy.publishedPackageSmoke.version, 'Release policy publishedPackageSmoke.version');
assertNullableString(policy.publishedPackageSmoke.registry, 'Release policy publishedPackageSmoke.registry');
readPublishedPackageSmokeConfig({}, [], policy.publishedPackageSmoke);
assertRequiredBeforePublishing(policy);
return policy;
}
function metadataNames(metadata, ecosystem) {
return metadata.filter((entry) => entry.ecosystem === ecosystem).map((entry) => entry.packageName);
}
function publishedPackageSmokeGate(policy) {
const config = readPublishedPackageSmokeConfig({}, [], policy.publishedPackageSmoke);
if (policy.releaseMode === PUBLISHED_PACKAGE_SMOKE_REQUIRED_RELEASE_MODE && !config.enabled) {
throw new Error(
'published-package-smoke-required release mode requires release-policy.json publishedPackageSmoke.packageName',
);
}
const base =
policy.releaseMode === CI_ARTIFACT_ONLY_RELEASE_MODE
? {
status: 'not_required',
reason: 'Published package smoke remains pending until release-policy.json enables npm registry publishing.',
}
: {
status: 'required',
reason: 'Run the published package smoke before accepting the hybrid-search release.',
};
return {
...base,
script: 'pnpm run release:published-smoke',
configSource: config.enabled ? config.configSource : null,
packageName: config.enabled ? config.packageName : null,
version: config.enabled ? config.packageVersion : policy.publishedPackageSmoke.version,
registry: config.enabled ? (config.registry ?? null) : policy.publishedPackageSmoke.registry,
};
}
function assertNonPublishingArtifactPolicy(policy, metadata) {
const policyLabel =
policy.releaseMode === CI_ARTIFACT_ONLY_RELEASE_MODE ? 'ci-artifact-only policy' : `${policy.releaseMode} policy`;
if (policy.npm.publish !== false) {
throw new Error(`${policyLabel} must keep npm.publish false`);
}
if (policy.python.publish !== false) {
throw new Error(`${policyLabel} must keep python.publish false`);
}
if (policy.npm.registry !== null) {
throw new Error(`${policyLabel} must keep npm.registry null`);
}
if (policy.python.repository !== null) {
throw new Error(`${policyLabel} must keep python.repository null`);
}
assertSameMembers(policy.npm.packages, metadataNames(metadata, 'npm'), 'Release policy npm.packages');
assertSameMembers(policy.python.packages, metadataNames(metadata, 'python'), 'Release policy python.packages');
for (const entry of metadata) {
if (entry.releaseMode !== CI_ARTIFACT_ONLY_RELEASE_MODE) {
throw new Error(`Package ${entry.packageName} releaseMode must remain ci-artifact-only`);
}
if (entry.ecosystem === 'npm') {
if (entry.private !== true) {
throw new Error(`${policyLabel} npm package ${entry.packageName} must remain private`);
}
if (!entry.packageVersion.endsWith('-private')) {
throw new Error(`${policyLabel} npm package ${entry.packageName} must use a private version suffix`);
}
}
}
}
export async function releaseReadinessReport(rootDir = scriptRootDir()) {
const policy = validateReleasePolicy(await readReleasePolicy(rootDir));
const layout = packageArtifactLayout(rootDir);
const manifest = await verifyArtifactManifest(layout);
const metadata = await packageReleaseMetadata(rootDir);
assertNonPublishingArtifactPolicy(policy, metadata);
return {
schemaVersion: 1,
releaseMode: policy.releaseMode,
sourceRevision: manifest.sourceRevision,
npmPublishEnabled: policy.npm.publish,
pythonPublishEnabled: policy.python.publish,
packageNames: metadata.map((entry) => entry.packageName),
publishedPackageSmokeGate: publishedPackageSmokeGate(policy),
blockedPublishingDecisions: policy.requiredBeforePublishing,
};
}
async function main() {
const report = await releaseReadinessReport();
if (process.argv.includes('--json')) {
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
return;
}
process.stdout.write(`KLO release mode: ${report.releaseMode}\n`);
process.stdout.write(`KLO source revision: ${report.sourceRevision ?? 'local'}\n`);
process.stdout.write(`KLO packages: ${report.packageNames.join(', ')}\n`);
process.stdout.write(`Published package smoke: ${report.publishedPackageSmokeGate.status}\n`);
process.stdout.write(`Published package smoke script: ${report.publishedPackageSmokeGate.script}\n`);
process.stdout.write(`Published package smoke reason: ${report.publishedPackageSmokeGate.reason}\n`);
process.stdout.write(`Published package smoke package: ${report.publishedPackageSmokeGate.packageName ?? 'not configured'}\n`);
process.stdout.write(`Published package smoke version: ${report.publishedPackageSmokeGate.version}\n`);
process.stdout.write(
`Published package smoke registry: ${report.publishedPackageSmokeGate.registry ?? 'default npm registry'}\n`,
);
process.stdout.write('Registry publishing remains disabled by release-policy.json.\n');
process.stdout.write('Required decisions before publishing:\n');
for (const decision of report.blockedPublishingDecisions) {
process.stdout.write(`- ${decision}\n`);
}
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
try {
await main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\n`);
process.exitCode = 1;
}
}

View file

@ -0,0 +1,376 @@
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, it } from 'node:test';
import { NPM_ARTIFACT_PACKAGES, packageArtifactLayout, writeArtifactManifest } from './package-artifacts.mjs';
import { readReleasePolicy, releasePolicyPath, releaseReadinessReport } from './release-readiness.mjs';
async function writeJson(path, value) {
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
}
async function writeReleaseMetadataInputs(root, options = {}) {
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
await mkdir(join(root, packageInfo.packageRoot), { recursive: true });
await writeJson(join(root, packageInfo.packageRoot, 'package.json'), {
name: packageInfo.name,
version: '0.0.0-private',
private:
packageInfo.name === '@klo/context'
? (options.contextPrivate ?? true)
: packageInfo.name === '@klo/cli'
? (options.cliPrivate ?? true)
: true,
});
}
await mkdir(join(root, 'python', 'klo-sl'), { recursive: true });
await mkdir(join(root, 'python', 'klo-daemon'), { recursive: true });
await writeFile(
join(root, 'python', 'klo-sl', 'pyproject.toml'),
['[project]', 'name = "klo-sl"', 'version = "0.1.0"', ''].join('\n'),
);
await writeFile(
join(root, 'python', 'klo-daemon', 'pyproject.toml'),
['[project]', 'name = "klo-daemon"', 'version = "0.1.0"', ''].join('\n'),
);
}
async function writeUploadableArtifactFixtures(layout) {
await mkdir(layout.npmDir, { recursive: true });
await mkdir(layout.pythonDir, { recursive: true });
const fileContents = new Map([
...NPM_ARTIFACT_PACKAGES.map((packageInfo) => [
layout.npmTarballs[packageInfo.name],
`${packageInfo.name}-tarball`,
]),
[join(layout.pythonDir, 'klo_sl-0.1.0-py3-none-any.whl'), 'klo-sl-wheel'],
[join(layout.pythonDir, 'klo_sl-0.1.0.tar.gz'), 'klo-sl-sdist'],
[join(layout.pythonDir, 'klo_daemon-0.1.0-py3-none-any.whl'), 'klo-daemon-wheel'],
[join(layout.pythonDir, 'klo_daemon-0.1.0.tar.gz'), 'klo-daemon-sdist'],
]);
for (const [path, contents] of fileContents) {
await writeFile(path, contents);
}
}
function releasePolicy(overrides = {}) {
const { npm: npmOverrides = {}, python: pythonOverrides = {}, ...policyOverrides } = overrides;
return {
schemaVersion: 1,
releaseMode: 'ci-artifact-only',
npm: {
publish: false,
registry: null,
packages: NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name),
...npmOverrides,
},
python: {
publish: false,
repository: null,
packages: ['klo-sl', 'klo-daemon'],
...pythonOverrides,
},
publishedPackageSmoke: {
packageName: null,
version: 'latest',
registry: null,
},
requiredBeforePublishing: [
'Choose npm registry and package visibility.',
'Choose Python package repository.',
'Choose public release versions.',
'Configure registry credentials outside source control.',
'Choose release tag and provenance policy.',
],
...policyOverrides,
};
}
async function writePolicy(root, policy = releasePolicy()) {
await writeJson(releasePolicyPath(root), policy);
}
async function writeReadyFixture(root, options = {}) {
await writeReleaseMetadataInputs(root, options);
await writePolicy(root, options.policy ?? releasePolicy());
const layout = packageArtifactLayout(root);
await writeUploadableArtifactFixtures(layout);
await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), {
sourceRevision: 'abc123',
});
return layout;
}
describe('release readiness policy', () => {
it('reads the checked release policy path from the KLO root', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-policy-test-'));
try {
const policy = releasePolicy();
await writePolicy(root, policy);
assert.equal(releasePolicyPath(root), join(root, 'release-policy.json'));
assert.deepEqual(await readReleasePolicy(root), policy);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('accepts the current ci-artifact-only policy, package metadata, and artifact manifest', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-ready-test-'));
try {
await writeReadyFixture(root);
const report = await releaseReadinessReport(root);
assert.deepEqual(report, {
schemaVersion: 1,
releaseMode: 'ci-artifact-only',
sourceRevision: 'abc123',
npmPublishEnabled: false,
pythonPublishEnabled: false,
packageNames: [...NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name), 'klo-sl', 'klo-daemon'],
publishedPackageSmokeGate: {
status: 'not_required',
script: 'pnpm run release:published-smoke',
reason: 'Published package smoke remains pending until release-policy.json enables npm registry publishing.',
configSource: null,
packageName: null,
version: 'latest',
registry: null,
},
blockedPublishingDecisions: [
'Choose npm registry and package visibility.',
'Choose Python package repository.',
'Choose public release versions.',
'Configure registry credentials outside source control.',
'Choose release tag and provenance policy.',
],
});
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('reports policy-controlled published package smoke config when present', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-config-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
publishedPackageSmoke: {
packageName: '@klo/cli-public',
version: '2026.5.8',
registry: 'https://registry.npmjs.org/',
},
}),
});
const report = await releaseReadinessReport(root);
assert.deepEqual(report.publishedPackageSmokeGate, {
status: 'not_required',
script: 'pnpm run release:published-smoke',
reason: 'Published package smoke remains pending until release-policy.json enables npm registry publishing.',
configSource: 'release-policy',
packageName: '@klo/cli-public',
version: '2026.5.8',
registry: 'https://registry.npmjs.org/',
});
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('reports required published package smoke when release mode requires it', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-required-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
releaseMode: 'published-package-smoke-required',
publishedPackageSmoke: {
packageName: '@klo/cli-public',
version: '2026.5.8',
registry: 'https://registry.npmjs.org/',
},
requiredBeforePublishing: [],
}),
});
const report = await releaseReadinessReport(root);
assert.deepEqual(report, {
schemaVersion: 1,
releaseMode: 'published-package-smoke-required',
sourceRevision: 'abc123',
npmPublishEnabled: false,
pythonPublishEnabled: false,
packageNames: [...NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name), 'klo-sl', 'klo-daemon'],
publishedPackageSmokeGate: {
status: 'required',
script: 'pnpm run release:published-smoke',
reason: 'Run the published package smoke before accepting the hybrid-search release.',
configSource: 'release-policy',
packageName: '@klo/cli-public',
version: '2026.5.8',
registry: 'https://registry.npmjs.org/',
},
blockedPublishingDecisions: [],
});
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects required published smoke mode without a package name', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-required-missing-config-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
releaseMode: 'published-package-smoke-required',
requiredBeforePublishing: [],
}),
});
await assert.rejects(
() => releaseReadinessReport(root),
/published-package-smoke-required release mode requires release-policy\.json publishedPackageSmoke\.packageName/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects required published smoke mode while publishing decisions remain', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-required-blocked-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
releaseMode: 'published-package-smoke-required',
publishedPackageSmoke: {
packageName: '@klo/cli-public',
version: 'latest',
registry: null,
},
}),
});
await assert.rejects(
() => releaseReadinessReport(root),
/published-package-smoke-required release mode requires requiredBeforePublishing to be empty/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects unsupported release modes', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-unsupported-mode-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
releaseMode: 'experimental-publish',
}),
});
await assert.rejects(
() => releaseReadinessReport(root),
/Unsupported release policy releaseMode: experimental-publish/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects publish-enabled npm policy while releaseMode is ci-artifact-only', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-npm-publish-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
npm: { publish: true, registry: 'https://registry.npmjs.org/' },
}),
});
await assert.rejects(
() => releaseReadinessReport(root),
/ci-artifact-only policy must keep npm.publish false/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects publish-enabled Python policy while releaseMode is ci-artifact-only', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-python-publish-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
python: { publish: true, repository: 'pypi' },
}),
});
await assert.rejects(
() => releaseReadinessReport(root),
/ci-artifact-only policy must keep python.publish false/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects unsafe release-policy published package smoke config', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-invalid-test-'));
try {
await writeReadyFixture(root, {
policy: releasePolicy({
publishedPackageSmoke: {
packageName: '@klo/cli public',
version: 'latest',
registry: null,
},
}),
});
await assert.rejects(
() => releaseReadinessReport(root),
/Invalid release-policy\.json publishedPackageSmoke\.packageName/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects a public npm package while releaseMode is ci-artifact-only', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-public-npm-test-'));
try {
await writeReadyFixture(root, { contextPrivate: false });
await assert.rejects(
() => releaseReadinessReport(root),
/ci-artifact-only policy npm package @klo\/context must remain private/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it('rejects stale artifacts before reporting release readiness', async () => {
const root = await mkdtemp(join(tmpdir(), 'klo-release-stale-artifact-test-'));
try {
const layout = await writeReadyFixture(root);
await writeFile(layout.cliTarball, 'changed-cli-tarball');
await assert.rejects(
() => releaseReadinessReport(root),
/Artifact manifest files do not match artifact contents/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});

175
scripts/run-klo.mjs Normal file
View file

@ -0,0 +1,175 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process';
import { constants } from 'node:fs';
import { access as fsAccess, readdir as fsReaddir, stat as fsStat } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
function kloRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
function cliBinPath(rootDir) {
return resolve(rootDir, 'packages', 'cli', 'dist', 'bin.js');
}
async function fileExists(path, access) {
try {
await access(path, constants.R_OK);
return true;
} catch {
return false;
}
}
async function packageBuildInputPaths(rootDir, readdir) {
const paths = [resolve(rootDir, 'package.json'), resolve(rootDir, 'tsconfig.base.json')];
let packageEntries = [];
try {
packageEntries = await readdir(resolve(rootDir, 'packages'), { withFileTypes: true });
} catch {
return paths;
}
for (const entry of packageEntries) {
if (!entry.isDirectory()) {
continue;
}
const packageDir = resolve(rootDir, 'packages', entry.name);
paths.push(resolve(packageDir, 'package.json'), resolve(packageDir, 'tsconfig.json'), resolve(packageDir, 'src'));
}
return paths;
}
async function newestMtimeMs(path, fs) {
let stats;
try {
stats = await fs.stat(path);
} catch {
return 0;
}
if (!stats.isDirectory()) {
return stats.mtimeMs;
}
let newest = stats.mtimeMs;
let entries = [];
try {
entries = await fs.readdir(path, { withFileTypes: true });
} catch {
return newest;
}
for (const entry of entries) {
newest = Math.max(newest, await newestMtimeMs(resolve(path, entry.name), fs));
}
return newest;
}
async function isBuildStale(rootDir, binPath, fs) {
let binStats;
try {
binStats = await fs.stat(binPath);
} catch {
return true;
}
const inputPaths = await packageBuildInputPaths(rootDir, fs.readdir);
for (const inputPath of inputPaths) {
if ((await newestMtimeMs(inputPath, fs)) > binStats.mtimeMs) {
return true;
}
}
return false;
}
function isShellCompletionRequest(argv) {
return argv[0] === '__complete' || (argv[0] === 'dev' && argv[1] === '__complete');
}
async function runBuffered(execFile, stdout, stderr, command, args, options) {
try {
const result = await execFile(command, args, { cwd: options.cwd, env: options.env, maxBuffer: 1024 * 1024 * 16 });
if (result.stdout) {
stdout.write(result.stdout);
}
if (result.stderr) {
stderr.write(result.stderr);
}
return 0;
} catch (error) {
if (typeof error?.stdout === 'string' && error.stdout.length > 0) {
stdout.write(error.stdout);
}
if (typeof error?.stderr === 'string' && error.stderr.length > 0) {
stderr.write(error.stderr);
}
return typeof error?.code === 'number' ? error.code : 1;
}
}
function runInherited(command, args, options) {
return new Promise((resolveExitCode) => {
const child = spawn(command, args, {
cwd: options.cwd,
stdio: 'inherit',
env: options.env ?? process.env,
});
child.on('error', (error) => {
process.stderr.write(`${error.message}\n`);
resolveExitCode(1);
});
child.on('exit', (code, signal) => {
if (code !== null) {
resolveExitCode(code);
return;
}
process.stderr.write(`Command terminated by signal ${signal ?? 'unknown'}\n`);
resolveExitCode(1);
});
});
}
export async function runWorkspaceKlo(argv, options = {}) {
const cliArgv = argv[0] === '--' ? argv.slice(1) : argv;
const rootDir = options.rootDir ?? kloRootDir();
const stdout = options.stdout ?? process.stdout;
const stderr = options.stderr ?? process.stderr;
const access = options.access ?? fsAccess;
const fs = {
stat: options.stat ?? fsStat,
readdir: options.readdir ?? fsReaddir,
};
const binPath = cliBinPath(rootDir);
const runCommand =
options.runCommand ??
(options.execFile
? (command, args, commandOptions) => runBuffered(options.execFile, stdout, stderr, command, args, commandOptions)
: (command, args, commandOptions) => runInherited(command, args, commandOptions));
const commandEnv = options.env;
const binExists = await fileExists(binPath, access);
const skipStaleBuildCheck = binExists && isShellCompletionRequest(cliArgv);
const needsBuild = !binExists || (!skipStaleBuildCheck && (await isBuildStale(rootDir, binPath, fs)));
if (needsBuild) {
stderr.write(
binExists
? 'KLO CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'
: 'KLO CLI build output is missing. Building it now with `pnpm run build`...\n',
);
const buildExitCode = await runCommand('pnpm', ['run', 'build'], { cwd: rootDir, env: commandEnv });
if (buildExitCode !== 0) {
stderr.write(
'\nKLO CLI build failed. Run `pnpm run setup:dev` from the KLO directory, then retry this command.\n',
);
return buildExitCode;
}
}
return await runCommand(process.execPath, [binPath, ...cliArgv], { cwd: rootDir, env: commandEnv });
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
process.exitCode = await runWorkspaceKlo(process.argv.slice(2));
}

243
scripts/run-klo.test.mjs Normal file
View file

@ -0,0 +1,243 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { runWorkspaceKlo } from './run-klo.mjs';
function freshBuildFs() {
return {
stat: async (path) => ({
mtimeMs: path.endsWith('/packages/cli/dist/bin.js') ? 2000 : 1000,
isDirectory: () => path.endsWith('/src') || path.endsWith('/packages'),
}),
readdir: async (path) => {
if (path.endsWith('/packages')) {
return [{ name: 'cli', isDirectory: () => true }];
}
if (path.endsWith('/src')) {
return [{ name: 'bin.ts', isDirectory: () => false }];
}
return [];
},
};
}
test('runWorkspaceKlo runs the built CLI when it already exists', async () => {
const calls = [];
const logs = [];
const fs = freshBuildFs();
const exitCode = await runWorkspaceKlo(['--version'], {
rootDir: '/workspace/klo',
access: async () => undefined,
stat: fs.stat,
readdir: fs.readdir,
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
return { stdout: '@klo/cli 0.0.0-private\n', stderr: '' };
},
stdout: { write: (chunk) => logs.push(['stdout', chunk]) },
stderr: { write: (chunk) => logs.push(['stderr', chunk]) },
});
assert.equal(exitCode, 0);
assert.deepEqual(calls, [
{
command: process.execPath,
args: ['/workspace/klo/packages/cli/dist/bin.js', '--version'],
cwd: '/workspace/klo',
},
]);
assert.deepEqual(logs, [['stdout', '@klo/cli 0.0.0-private\n']]);
});
test('runWorkspaceKlo forwards a caller-provided environment to buffered commands', async () => {
const calls = [];
const fs = freshBuildFs();
const exitCode = await runWorkspaceKlo(['--version'], {
rootDir: '/workspace/klo',
access: async () => undefined,
stat: fs.stat,
readdir: fs.readdir,
env: { PATH: '/bin', GIT_CEILING_DIRECTORIES: '/workspace/klo/examples' },
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd, env: options.env });
return { stdout: '@klo/cli 0.0.0-private\n', stderr: '' };
},
stdout: { write: () => undefined },
stderr: { write: () => undefined },
});
assert.equal(exitCode, 0);
assert.deepEqual(calls, [
{
command: process.execPath,
args: ['/workspace/klo/packages/cli/dist/bin.js', '--version'],
cwd: '/workspace/klo',
env: { PATH: '/bin', GIT_CEILING_DIRECTORIES: '/workspace/klo/examples' },
},
]);
});
test('runWorkspaceKlo drops a leading npm argument separator', async () => {
const calls = [];
const fs = freshBuildFs();
const exitCode = await runWorkspaceKlo(['--', 'connection', 'test', 'warehouse', '--help'], {
rootDir: '/workspace/klo',
access: async () => undefined,
stat: fs.stat,
readdir: fs.readdir,
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
return { stdout: 'Usage: klo connection test\n', stderr: '' };
},
stdout: { write: () => undefined },
stderr: { write: () => undefined },
});
assert.equal(exitCode, 0);
assert.deepEqual(calls, [
{
command: process.execPath,
args: ['/workspace/klo/packages/cli/dist/bin.js', 'connection', 'test', 'warehouse', '--help'],
cwd: '/workspace/klo',
},
]);
});
test('runWorkspaceKlo skips stale-build checks for shell completion when dist exists', async () => {
const calls = [];
let statCalls = 0;
const exitCode = await runWorkspaceKlo(['dev', '__complete', '--shell', 'zsh', '--position', '2', '--', 'klo', ''], {
rootDir: '/workspace/klo',
access: async () => undefined,
stat: async (path) => {
statCalls += 1;
return {
mtimeMs: path.endsWith('/packages/cli/dist/bin.js') ? 2000 : 3000,
isDirectory: () => path.endsWith('/src') || path.endsWith('/packages'),
};
},
readdir: async () => {
throw new Error('completion should not scan source directories');
},
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
return { stdout: 'connect:Add, list, test, and map data sources\n', stderr: '' };
},
stdout: { write: () => undefined },
stderr: { write: () => undefined },
});
assert.equal(exitCode, 0);
assert.equal(statCalls, 0);
assert.deepEqual(calls, [
{
command: process.execPath,
args: [
'/workspace/klo/packages/cli/dist/bin.js',
'dev',
'__complete',
'--shell',
'zsh',
'--position',
'2',
'--',
'klo',
'',
],
cwd: '/workspace/klo',
},
]);
});
test('runWorkspaceKlo builds the workspace CLI before running it when dist is missing', async () => {
const calls = [];
const logs = [];
let binExists = false;
const exitCode = await runWorkspaceKlo(['setup', 'demo', '--mode', 'replay', '--no-input', '--viz'], {
rootDir: '/workspace/klo',
access: async () => {
if (!binExists) {
throw Object.assign(new Error('missing'), { code: 'ENOENT' });
}
},
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
if (command === 'pnpm') {
binExists = true;
return { stdout: 'build ok\n', stderr: '' };
}
return { stdout: 'Replay complete\n', stderr: '' };
},
stdout: { write: (chunk) => logs.push(['stdout', chunk]) },
stderr: { write: (chunk) => logs.push(['stderr', chunk]) },
});
assert.equal(exitCode, 0);
assert.deepEqual(
calls.map((call) => [call.command, call.args]),
[
['pnpm', ['run', 'build']],
[
process.execPath,
['/workspace/klo/packages/cli/dist/bin.js', 'setup', 'demo', '--mode', 'replay', '--no-input', '--viz'],
],
],
);
assert.deepEqual(logs, [
['stderr', 'KLO CLI build output is missing. Building it now with `pnpm run build`...\n'],
['stdout', 'build ok\n'],
['stdout', 'Replay complete\n'],
]);
});
test('runWorkspaceKlo rebuilds before running when workspace sources are newer than dist', async () => {
const calls = [];
const logs = [];
let sourceMtimeMs = 3000;
const exitCode = await runWorkspaceKlo(['dev', 'scan', 'orbit', '--enrich'], {
rootDir: '/workspace/klo',
access: async () => undefined,
stat: async (path) => ({
mtimeMs: path.endsWith('/packages/cli/dist/bin.js') ? 2000 : sourceMtimeMs,
isDirectory: () => path.endsWith('/src') || path.endsWith('/packages'),
}),
readdir: async (path) => {
if (path.endsWith('/packages')) {
return [{ name: 'context', isDirectory: () => true }];
}
if (path.endsWith('/src')) {
return [{ name: 'scan.ts', isDirectory: () => false }];
}
return [];
},
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
if (command === 'pnpm') {
sourceMtimeMs = 1000;
return { stdout: 'build ok\n', stderr: '' };
}
return { stdout: 'scan ok\n', stderr: '' };
},
stdout: { write: (chunk) => logs.push(['stdout', chunk]) },
stderr: { write: (chunk) => logs.push(['stderr', chunk]) },
});
assert.equal(exitCode, 0);
assert.deepEqual(
calls.map((call) => [call.command, call.args]),
[
['pnpm', ['run', 'build']],
[process.execPath, ['/workspace/klo/packages/cli/dist/bin.js', 'dev', 'scan', 'orbit', '--enrich']],
],
);
assert.deepEqual(logs, [
['stderr', 'KLO CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'],
['stdout', 'build ok\n'],
['stdout', 'scan ok\n'],
]);
});

74
scripts/setup-dev.mjs Normal file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env node
import { execFile as execFileCallback } from 'node:child_process';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFileCallback);
function kloRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
function failureText(error) {
const stdout = typeof error?.stdout === 'string' ? error.stdout.trim() : '';
const stderr = typeof error?.stderr === 'string' ? error.stderr.trim() : '';
const message = error instanceof Error ? error.message.trim() : String(error);
return [stderr, stdout, message].find((line) => line.length > 0) ?? 'Command failed';
}
export async function runSetupDev(options = {}) {
const rootDir = options.rootDir ?? kloRootDir();
const execFile = options.execFile ?? execFileAsync;
const log = options.log ?? ((line) => process.stdout.write(`${line}\n`));
const phases = [
{
name: 'dependency install',
command: 'pnpm',
args: ['install', '--frozen-lockfile'],
retry: 'pnpm install --frozen-lockfile',
},
{
name: 'native SQLite rebuild',
command: 'pnpm',
args: ['run', 'native:rebuild'],
retry: 'pnpm run native:rebuild',
},
{
name: 'TypeScript package build',
command: 'pnpm',
args: ['run', 'build'],
retry: 'pnpm run build',
},
{
name: 'doctor setup',
command: process.execPath,
args: ['packages/cli/dist/bin.js', 'dev', 'doctor', 'setup', '--no-input'],
retry: 'pnpm run klo -- dev doctor setup --no-input',
},
];
for (const phase of phases) {
log(`RUN ${phase.name}: ${phase.command} ${phase.args.join(' ')}`);
try {
await execFile(phase.command, phase.args, { cwd: rootDir, maxBuffer: 1024 * 1024 });
log(`PASS ${phase.name}`);
} catch (error) {
log(`FAIL ${phase.name}: ${failureText(error)}`);
log(`Retry: ${phase.retry}`);
return { ok: false, failedPhase: phase };
}
}
log('Workspace CLI: pnpm run klo -- --help');
log('Optional global dev link: pnpm run link:dev');
return { ok: true };
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
const result = await runSetupDev();
if (!result.ok) {
process.exitCode = 1;
}
}

View file

@ -0,0 +1,56 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { runSetupDev } from './setup-dev.mjs';
test('runSetupDev runs phased setup without global linking', async () => {
const calls = [];
const logs = [];
const result = await runSetupDev({
rootDir: '/workspace/klo',
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
return { stdout: `${command} ${args.join(' ')}`, stderr: '' };
},
log: (line) => logs.push(line),
});
assert.equal(result.ok, true);
assert.deepEqual(
calls.map((call) => [call.command, call.args]),
[
['pnpm', ['install', '--frozen-lockfile']],
['pnpm', ['run', 'native:rebuild']],
['pnpm', ['run', 'build']],
[process.execPath, ['packages/cli/dist/bin.js', 'dev', 'doctor', 'setup', '--no-input']],
],
);
assert.equal(calls.some((call) => call.args.includes('link')), false);
assert.equal(logs.some((line) => line.includes('PASS doctor setup')), true);
});
test('runSetupDev stops at the failed phase and prints a retry command', async () => {
const calls = [];
const logs = [];
const result = await runSetupDev({
rootDir: '/workspace/klo',
execFile: async (command, args) => {
calls.push({ command, args });
if (args.includes('native:rebuild')) {
const error = new Error('native rebuild failed');
error.stdout = '';
error.stderr = 'better-sqlite3 rebuild failed';
throw error;
}
return { stdout: '', stderr: '' };
},
log: (line) => logs.push(line),
});
assert.equal(result.ok, false);
assert.equal(result.failedPhase.name, 'native SQLite rebuild');
assert.equal(result.failedPhase.retry, 'pnpm run native:rebuild');
assert.equal(calls.length, 2);
assert.equal(logs.some((line) => line.includes('Retry: pnpm run native:rebuild')), true);
});

View file

@ -0,0 +1,67 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { describe, it } from 'node:test';
async function readText(relativePath) {
return readFile(new URL(`../${relativePath}`, import.meta.url), 'utf8');
}
function assertIncludesAll(text, values) {
for (const value of values) {
assert.match(text, new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
}
}
describe('standalone KLO CI workflow', () => {
it('runs the package checks from a filtered repository root', async () => {
const workflow = await readText('.github/workflows/ci.yml');
assert.match(workflow, /^name: KLO CI/m);
assertIncludesAll(workflow, [
'permissions:',
'contents: read',
'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd',
'pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061',
'actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238',
'node-version: "24"',
'cache-dependency-path: "pnpm-lock.yaml"',
'pnpm install --frozen-lockfile',
'pnpm run check',
'actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405',
'python-version: "3.13"',
'astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b',
'cache-dependency-glob: "uv.lock"',
'uv sync --all-packages',
'uv run pytest',
'pnpm run artifacts:check',
]);
assert.doesNotMatch(workflow, /sparse-checkout/);
assert.doesNotMatch(workflow, /cd klo/);
assert.doesNotMatch(workflow, /klo\/pnpm-lock\.yaml/);
assert.doesNotMatch(workflow, /klo\/uv\.lock/);
});
it('uploads verified artifacts from root-relative paths', async () => {
const workflow = await readText('.github/workflows/ci.yml');
assertIncludesAll(workflow, [
'actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f',
'name: klo-package-artifacts-${{ github.sha }}',
'dist/artifacts/manifest.json',
'dist/artifacts/npm/*.tgz',
'dist/artifacts/python/*.whl',
'dist/artifacts/python/*.tar.gz',
'if-no-files-found: error',
'retention-days: 7',
]);
assert.doesNotMatch(workflow, /klo\/dist\/artifacts/);
});
it('syncs injected workspace packages after package builds', async () => {
const workspace = await readText('pnpm-workspace.yaml');
assert.match(workspace, /syncInjectedDepsAfterScripts:\n\s+- build/);
});
});

View file

@ -0,0 +1,98 @@
#!/usr/bin/env node
import { readFileSync } from 'node:fs';
const [backend, filePath] = process.argv.slice(2);
function usage() {
process.stderr.write('Usage: node klo/scripts/validate-llm-debug-jsonl.mjs anthropic|vertex /path/to/debug.jsonl\n');
}
function fail(message) {
process.stderr.write(`${message}\n`);
process.exit(1);
}
if (!['anthropic', 'vertex'].includes(backend) || !filePath) {
usage();
process.exit(2);
}
const raw = readFileSync(filePath, 'utf8').trim();
if (!raw) {
fail(`debug JSONL is empty: ${filePath}`);
}
const records = raw.split(/\n+/).map((line, index) => {
try {
return JSON.parse(line);
} catch (error) {
throw new Error(`line ${index + 1} is not valid JSON: ${error.message}`);
}
});
const serialized = JSON.stringify(records);
const bannedKeyPattern = /"(content|text|prompt|toolSchema|parameters|apiKey|api_key|password|token)"\s*:/i;
if (bannedKeyPattern.test(serialized)) {
fail('debug JSONL contains a prompt, schema, credential, or token-shaped field');
}
const providerOptionEntries = records.flatMap((record) => {
if (!Array.isArray(record.providerOptions)) {
throw new Error(`record ${record.operationName ?? '<unknown>'} is missing providerOptions array`);
}
return record.providerOptions;
});
const cacheMarkerEntries = providerOptionEntries.filter((entry) => {
return JSON.stringify(entry.providerOptions).includes('"cacheControl"');
});
if (cacheMarkerEntries.length === 0) {
fail('no cacheControl providerOptions were recorded');
}
const requiredMarkerTargets = ['message', 'message-part', 'tool'];
const markerTargets = new Set(cacheMarkerEntries.map((entry) => entry.target));
for (const target of requiredMarkerTargets) {
if (!markerTargets.has(target)) {
fail(`missing cacheControl marker target: ${target}`);
}
}
const ttlValues = new Set();
for (const marker of cacheMarkerEntries) {
const markerJson = JSON.stringify(marker.providerOptions);
for (const match of markerJson.matchAll(/"ttl":"([^"]+)"/g)) {
ttlValues.add(match[1]);
}
}
if (ttlValues.size === 0) {
fail('cacheControl markers did not expose ttl values');
}
for (const ttl of ttlValues) {
if (ttl !== '1h' && ttl !== '5m') {
fail(`unexpected cache ttl: ${ttl}`);
}
}
if (backend === 'vertex' && !ttlValues.has('1h')) {
fail('vertex debug capture did not include a default 1h cache marker');
}
if (backend === 'vertex' && serialized.includes('extended-cache-ttl-2025-04-11')) {
fail('vertex debug capture included the direct-Anthropic extended cache TTL beta header');
}
process.stdout.write(
`${JSON.stringify({
backend,
records: records.length,
providerOptionEntries: providerOptionEntries.length,
cacheMarkerEntries: cacheMarkerEntries.length,
markerTargets: [...markerTargets].sort(),
ttlValues: [...ttlValues].sort(),
})}\n`,
);

View file

@ -0,0 +1,112 @@
import { spawnSync } from 'node:child_process';
import assert from 'node:assert/strict';
import { mkdtempSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { test } from 'node:test';
const scriptPath = new URL('./validate-llm-debug-jsonl.mjs', import.meta.url).pathname;
function runValidator(args) {
return spawnSync(process.execPath, [scriptPath, ...args], {
encoding: 'utf8',
});
}
function writeDebugJsonl(records) {
const dir = mkdtempSync(join(tmpdir(), 'klo-llm-debug-validator-'));
const filePath = join(dir, 'debug.jsonl');
writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join('\n')}\n`, 'utf8');
return filePath;
}
const validRecord = {
operationName: 'ingest-bundle-wu',
modelRole: 'candidateExtraction',
modelId: 'claude-sonnet-4-6',
messageCount: 2,
toolNames: ['emit_candidate'],
providerOptions: [
{
target: 'message',
index: 0,
role: 'system',
providerOptions: { anthropic: { cacheControl: { type: 'ephemeral', ttl: '1h' } } },
},
{
target: 'message-part',
index: 1,
role: 'user',
partIndex: 0,
providerOptions: { anthropic: { cacheControl: { type: 'ephemeral', ttl: '5m' } } },
},
{
target: 'tool',
name: 'emit_candidate',
providerOptions: { anthropic: { cacheControl: { type: 'ephemeral', ttl: '1h' } } },
},
],
};
test('prints usage and exits 2 when required arguments are missing', () => {
const result = runValidator([]);
assert.equal(result.status, 2);
assert.match(result.stderr, /Usage: node klo\/scripts\/validate-llm-debug-jsonl\.mjs anthropic\|vertex/);
});
test('accepts sanitized debug JSONL with message, message-part, and tool cache markers', () => {
const filePath = writeDebugJsonl([validRecord]);
const result = runValidator(['anthropic', filePath]);
assert.equal(result.status, 0, result.stderr);
const parsed = JSON.parse(result.stdout);
assert.equal(parsed.backend, 'anthropic');
assert.equal(parsed.records, 1);
assert.equal(parsed.providerOptionEntries, 3);
assert.equal(parsed.cacheMarkerEntries, 3);
assert.deepEqual(parsed.markerTargets, ['message', 'message-part', 'tool']);
assert.deepEqual(parsed.ttlValues, ['1h', '5m']);
});
test('rejects debug JSONL that lacks nested message-part cache marker evidence', () => {
const filePath = writeDebugJsonl([
{
...validRecord,
providerOptions: validRecord.providerOptions.filter((entry) => entry.target !== 'message-part'),
},
]);
const result = runValidator(['anthropic', filePath]);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /missing cacheControl marker target: message-part/);
});
test('rejects prompt-shaped fields in debug JSONL', () => {
const filePath = writeDebugJsonl([{ ...validRecord, text: 'SECRET PROMPT' }]);
const result = runValidator(['anthropic', filePath]);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /prompt, schema, credential, or token-shaped field/);
});
test('rejects direct-Anthropic extended cache beta header in Vertex debug summaries', () => {
const filePath = writeDebugJsonl([
{
...validRecord,
providerOptions: [
...validRecord.providerOptions,
{
target: 'message',
index: 0,
role: 'system',
providerOptions: { header: 'extended-cache-ttl-2025-04-11' },
},
],
},
]);
const result = runValidator(['vertex', filePath]);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /direct-Anthropic extended cache TTL beta header/);
});