feat(core): add upstream provenance registry validation
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import type { UpstreamCapabilityRecord } from "@siax/c0py-types";
|
||||
|
||||
const COPyleft_ISOLATED = new Set(["AGPL-3.0", "AGPL-3.0-only", "AGPL-3.0-or-later"]);
|
||||
|
||||
export interface ProvenanceValidationResult {
|
||||
valid: boolean;
|
||||
errors: string[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export function validateUpstreamCapability(
|
||||
record: UpstreamCapabilityRecord,
|
||||
): ProvenanceValidationResult {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
const source = record.source;
|
||||
|
||||
if (!record.capability.trim()) errors.push("capability is required");
|
||||
if (!record.rationale.trim()) errors.push("rationale is required");
|
||||
if (record.disposition !== "DROP" && record.tests.length === 0) {
|
||||
errors.push("at least one verification test is required");
|
||||
}
|
||||
|
||||
if (["CHERRY_PICK", "REFERENCE_ONLY", "REIMPLEMENT"].includes(record.disposition)) {
|
||||
if (!source.repository) errors.push("source.repository is required");
|
||||
if (!source.commit) errors.push("source.commit is required");
|
||||
}
|
||||
|
||||
if (record.disposition === "CHERRY_PICK") {
|
||||
if (!source.sourcePaths?.length) errors.push("source.sourcePaths is required for CHERRY_PICK");
|
||||
if (!source.license) errors.push("source.license is required for CHERRY_PICK");
|
||||
}
|
||||
|
||||
if (source.license && COPyleft_ISOLATED.has(source.license) && record.disposition === "CHERRY_PICK") {
|
||||
errors.push("AGPL/copy-left source must remain behind an adapter/service boundary; do not cherry-pick into core");
|
||||
}
|
||||
|
||||
if (source.license && COPyleft_ISOLATED.has(source.license) && record.disposition !== "ADAPTER") {
|
||||
warnings.push("copyleft source should normally be classified as ADAPTER");
|
||||
}
|
||||
|
||||
return { valid: errors.length === 0, errors, warnings };
|
||||
}
|
||||
|
||||
export function assertUpstreamCapability(record: UpstreamCapabilityRecord): void {
|
||||
const result = validateUpstreamCapability(record);
|
||||
if (!result.valid) {
|
||||
throw new Error(`Invalid upstream capability ${record.id}: ${result.errors.join("; ")}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user