/**
* Convert ProvenanceRecord[] into LintDiagnostic[] for SARIF output.
*
* Mapping:
* - category "needs-review" → severity "warning" (or "error" if --strict)
* - category "skipped" → severity "info"
* - category "action-map" tier 3 → severity "warning"
* - all others → no diagnostic (clean translation)
*/
import type { LintDiagnostic } from "@intentius/chant/lint/rule";
import type { ProvenanceRecord } from "./provenance";
export interface DiagnosticOptions {
/** When true, needs-review records become errors instead of warnings */
strict?: boolean;
}
export function provenanceToDiagnostics(
records: ProvenanceRecord[],
opts: DiagnosticOptions = {},
): LintDiagnostic[] {
const out: LintDiagnostic[] = [];
for (const r of records) {
// Security records always emit a diagnostic, at their own severity
// (escalated to error under --strict when the property was lost or needs
// review), independent of the functional category.
if (r.security) {
const sev =
opts.strict && (r.security.fate === "lost" || r.security.fate === "needs-review")
? "error"
: r.security.severity;
out.push({
file: r.sourceFile ?? "",
line: r.sourceLine ?? 1,
column: r.sourceColumn ?? 1,
ruleId: r.rule,
severity: sev,
message: r.note ?? r.rule,
});
continue;
}
let severity: "error" | "warning" | "info" | undefined;
if (r.category === "needs-review") {
severity = opts.strict ? "error" : "warning";
} else if (r.category === "skipped") {
severity = "info";
} else if (r.category === "action-map" && r.mappingTier === 3) {
severity = "warning";
} else {
continue;
}
out.push({
file: r.sourceFile ?? "",
line: r.sourceLine ?? 1,
column: r.sourceColumn ?? 1,
ruleId: r.rule,
severity,
message: r.note ?? r.rule,
});
}
return out;
}