Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
import path from 'node:path';
import { existsSync } from 'node:fs';
import { loadConfig } from '../config.js';
import { runErc, runDrc } from '../kicad/cli.js';
import { formatViolations, type CheckReport } from '../kicad/report.js';
import { checkDrift, emptySchematicWarning, type DriftMismatch } from '../memory/drift.js';
import { loadConstraints, checkForbiddenPins, type ConstraintViolation } from '../memory/constraints.js';
import { pinNets } from '../kicad/sexp.js';
import { openspecValidate } from '../openspec/cli.js';
/**
* `copperhead check` (alias `verify`): deterministic, zero LLM calls, CI-safe
* (AC-2). This module must never import a provider.
*/
export interface CheckResult {
ok: boolean;
erc: { ok: boolean; violations: number } | null;
drc: { ok: boolean; violations: number } | null;
drift: { ok: boolean; mismatches: DriftMismatch[]; warning?: string };
openspec: { ok: boolean; detail: string } | null;
constraints: { ok: boolean; violations: ConstraintViolation[] };
}
export async function runCheck(repoRoot: string, log: (s: string) => void): Promise<CheckResult> {
const config = await loadConfig(repoRoot);
let erc: CheckReport | null = null;
let drc: CheckReport | null = null;
if (config.schematic && existsSync(path.join(repoRoot, config.schematic))) {
erc = await runErc(path.join(repoRoot, config.schematic));
log(erc.ok ? 'ERC ✓' : formatViolations(erc));
} else {
log('ERC skipped (no schematic configured; run copperhead init)');
}
if (config.board && existsSync(path.join(repoRoot, config.board))) {
drc = await runDrc(path.join(repoRoot, config.board));
log(drc.ok ? 'DRC ✓' : formatViolations(drc));
} else {
log('DRC skipped (no board configured)');
}
let drift: DriftMismatch[] = [];
let driftWarning: string | null = null;
if (config.schematic && existsSync(path.join(repoRoot, config.schematic))) {
drift = await checkDrift(repoRoot, config.docs, config.schematic);
log(drift.length === 0 ? 'drift ✓' : drift.map((m) => `drift: ${m.doc} claims "${m.claim}" but actual is "${m.actual}"`).join('\n'));
// Informational, never a failure: the zero-symbol drift exemption is for
// bootstrap, but an established repo that lost its schematic content
// deserves a visible note rather than a silent green.
driftWarning = await emptySchematicWarning(repoRoot, config.docs, config.schematic);
if (driftWarning) log(`drift warning: ${driftWarning}`);
}
let openspec: { ok: boolean; detail: string } | null = null;
if (existsSync(path.join(repoRoot, 'openspec', 'config.yaml'))) {
const res = await openspecValidate(repoRoot);
openspec = { ok: res.ok, detail: res.output };
log(res.ok ? 'openspec ✓' : `openspec: ${res.output}`);
}
let constraintViolations: ConstraintViolation[] = [];
if (config.schematic && existsSync(path.join(repoRoot, config.schematic))) {
const registry = await loadConstraints(repoRoot);
const pins = await pinNets(path.join(repoRoot, config.schematic));
constraintViolations = checkForbiddenPins(registry, pins);
if (Object.keys(registry).length) {
log(
constraintViolations.length === 0
? 'constraints ✓'
: constraintViolations.map((v) => `constraint ${v.key}: ${v.description} (source: ${v.source})`).join('\n'),
);
}
}
const ok =
(erc?.ok ?? true) &&
(drc?.ok ?? true) &&
drift.length === 0 &&
(openspec?.ok ?? true) &&
constraintViolations.length === 0;
return {
ok,
erc: erc ? { ok: erc.ok, violations: erc.violations.length } : null,
drc: drc ? { ok: drc.ok, violations: drc.violations.length } : null,
drift: { ok: drift.length === 0, mismatches: drift, ...(driftWarning ? { warning: driftWarning } : {}) },
openspec,
constraints: { ok: constraintViolations.length === 0, violations: constraintViolations },
};
}
3aa468eadThis file is pinned to the selected release. Download the original to open it in your design tool.