refactor(context): validate ktx.yaml with Zod and surface issues in status (#91)

* refactor(context): validate ktx.yaml with Zod and surface issues in status

- Replace hand-rolled ktx.yaml parsing with a strict Zod schema and
  derive KtxProjectConfig types from it.
- Add validateKtxProjectConfig returning structured KtxConfigIssue[]
  with migration hints for deprecated keys (ingest.llm,
  scan.enrichment.backend, etc.).
- Wire ktx status/doctor to run validation, render schema issues in
  plain and JSON output, and add a Config row to project status.
- Update the orbit example to camelCase scan.relationships keys to
  match the schema.

* fix(context): tolerate legacy setup.completed_steps and optional driver

- Accept and drop the legacy setup.completed_steps field so existing
  ktx.yaml files migrated from older versions still load.
- Make connections.<id>.driver optional in the schema; runtime code
  already produces a clear "no driver" error at use time.

* feat(cli): add ktx status --validate to run only ktx.yaml schema validation

- New --validate flag dispatches a focused runKtxDoctor 'validate' branch
  that reads ktx.yaml, runs validateKtxProjectConfig, and skips LLM,
  connection, embedding, and query-history checks.
- Plain output prints a single Config row; JSON output emits
  {ok: true} on success or the existing invalid_config / missing_project
  shapes on failure.
This commit is contained in:
Andrey Avtomonov 2026-05-14 15:36:35 +02:00 committed by GitHub
parent 49f1e2720e
commit b3be54e3fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 783 additions and 545 deletions

View file

@ -1,4 +1,5 @@
import type {
KtxConfigIssue,
KtxLocalProject,
KtxProjectConfig,
KtxProjectConnectionConfig,
@ -56,6 +57,12 @@ interface StorageStatus {
gitAuthor: string;
}
interface ConfigStatus {
status: ProjectStatusLevel;
detail: string;
issues: KtxConfigIssue[];
}
interface WarningItem {
message: string;
fix?: string;
@ -72,6 +79,7 @@ function hasOwnField(value: Record<string, unknown>, key: string): boolean {
export interface ProjectStatus {
projectName: string;
projectDir: string;
config: ConfigStatus;
llm: LlmStatus;
embeddings: EmbeddingsStatus;
storage: StorageStatus;
@ -610,12 +618,26 @@ function buildVerdict(
export interface BuildProjectStatusOptions {
env?: NodeJS.ProcessEnv;
postgresQueryHistoryProbe?: PostgresQueryHistoryProbe;
configIssues?: KtxConfigIssue[];
}
function buildConfigStatus(issues: KtxConfigIssue[] | undefined): ConfigStatus {
const list = issues ?? [];
if (list.length === 0) {
return { status: 'ok', detail: 'ktx.yaml schema valid', issues: [] };
}
return {
status: 'warn',
detail: `${list.length} issue${list.length === 1 ? '' : 's'} in ktx.yaml`,
issues: list,
};
}
export async function buildProjectStatus(project: KtxLocalProject, options: BuildProjectStatusOptions = {}): Promise<ProjectStatus> {
const env = options.env ?? process.env;
const config = project.config;
const configStatus = buildConfigStatus(options.configIssues);
const llm = buildLlmStatus(config.llm, env);
const embeddings = buildEmbeddingsStatus(config.ingest.embeddings, env);
const storage = buildStorageStatus(config);
@ -630,6 +652,7 @@ export async function buildProjectStatus(project: KtxLocalProject, options: Buil
return {
projectName: config.project,
projectDir: project.projectDir,
config: configStatus,
llm,
embeddings,
storage,
@ -719,6 +742,13 @@ export function renderProjectStatus(status: ProjectStatus, options: RenderProjec
lines.push(` ${label('Embeddings')} ${embedDetail} ${sym(status.embeddings.status)} ${dim(status.embeddings.detail)}`);
lines.push(` ${label('Storage')} ${dim(`${status.storage.state} (state) · ${status.storage.search} (search)`)}`);
lines.push(` ${label('Config')} ${sym(status.config.status)} ${dim(status.config.detail)}`);
if (status.config.issues.length > 0) {
for (const issue of status.config.issues) {
lines.push(` ${color('warn', SYMBOL.warn)} ${issue.message}`);
if (issue.fix) lines.push(` ${dim(`${issue.fix}`)}`);
}
}
lines.push('');
// Connections