/*
profile validate — validate a certification profile and emit diagnostics.
- Do not score — validation checks profile shape, not quality.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
- ADR-0083: CERT-PROFILE-02 is test-only — leitstand.certify uses astroCertificationProfile directly and never builds ctx.profileId from a site forge.yaml. If wired to a live command, sites need profile: site or a site default.
*/
import type {
CertificationProfileV1,
SiteQualityDimension,
CertificationRequirementV1,
} from "./schemas.ts";
export interface ProfileValidationDiagnostic {
ruleId: string;
severity: "error";
message: string;
}
export interface ProfileValidationResultV1 {
valid: boolean;
diagnostics: ProfileValidationDiagnostic[];
dimensionCoverage: Array<{
dimension: SiteQualityDimension | "core";
hasMainRequired: boolean;
requirementIds: string[];
}>;
}
const ALL_DIMENSIONS: SiteQualityDimension[] = [
"candidate-integrity",
"business-truth-compliance",
"editorial-localization",
"information-architecture-discoverability",
"ux-conversion",
"visual-accessibility",
"performance-runtime",
"security-operational-readiness",
"independent-qualitative-evaluation",
];
export interface ProfileValidationContextV1 {
pluginId: string;
profileId: string;
registeredCommands: Set;
}
export function validateCertificationProfileV1(
profile: CertificationProfileV1,
ctx: ProfileValidationContextV1,
): ProfileValidationResultV1 {
const diagnostics: ProfileValidationDiagnostic[] = [];
if (profile.plugin.id !== ctx.pluginId) {
diagnostics.push({
ruleId: "CERT-PROFILE-02",
severity: "error",
message: `plugin.id "${profile.plugin.id}" does not match active plugin "${ctx.pluginId}"`,
});
}
if (profile.plugin.profileId !== ctx.profileId) {
diagnostics.push({
ruleId: "CERT-PROFILE-02",
severity: "error",
message: `plugin.profileId "${profile.plugin.profileId}" does not match forge.yaml profile "${ctx.profileId}"`,
});
}
const requirementIds = new Set();
for (const req of profile.requirements) {
if (requirementIds.has(req.id)) {
diagnostics.push({
ruleId: "CERT-PROFILE-03",
severity: "error",
message: `duplicate requirement ID "${req.id}"`,
});
}
requirementIds.add(req.id);
}
const producerIds = new Set();
for (const [id, producer] of Object.entries(profile.producers)) {
if (producerIds.has(id)) {
diagnostics.push({
ruleId: "CERT-PROFILE-03",
severity: "error",
message: `duplicate producer ID "${id}"`,
});
}
producerIds.add(id);
if (producer.kind === "kernel-command" && producer.command) {
if (!ctx.registeredCommands.has(producer.command)) {
diagnostics.push({
ruleId: "CERT-PROFILE-04",
severity: "error",
message: `producer "${id}" references unregistered command "${producer.command}"`,
});
}
}
}
for (const req of profile.requirements) {
if (req.classification === "required" || req.classification === "conditional") {
if (!profile.producers[req.producerId]) {
diagnostics.push({
ruleId: "CERT-PROFILE-05",
severity: "error",
message: `requirement "${req.id}" references missing producer "${req.producerId}"`,
});
}
}
if (req.classification === "advisory" && !profile.producers[req.producerId]) {
diagnostics.push({
ruleId: "CERT-PROFILE-05",
severity: "error",
message: `advisory requirement "${req.id}" references missing producer "${req.producerId}"`,
});
}
if (req.reuse.environmentIndependent) {
const allowedSet = new Set(req.reuse.allowedFrom);
if (allowedSet.size === 0) {
diagnostics.push({
ruleId: "CERT-PROFILE-06",
severity: "error",
message: `requirement "${req.id}" declares environmentIndependent but has empty allowedFrom`,
});
}
}
if (req.normativeRefs.length === 0) {
diagnostics.push({
ruleId: "CERT-PROFILE-07",
severity: "error",
message: `requirement "${req.id}" has no normative reference`,
});
}
if (req.remediation.reproduceCommand.length === 0) {
diagnostics.push({
ruleId: "CERT-PROFILE-07",
severity: "error",
message: `requirement "${req.id}" has empty reproduceCommand`,
});
}
if (req.driftAction === "rollback" && req.classification !== "required") {
diagnostics.push({
ruleId: "CERT-PROFILE-08",
severity: "error",
message: `requirement "${req.id}" has rollback driftAction but is not required classification`,
});
}
if (req.gates.includes("continuous-health")) {
if (req.freshness.maxAgeSeconds === null && !req.freshness.schedule) {
diagnostics.push({
ruleId: "CERT-PROFILE-09",
severity: "error",
message: `continuous-health requirement "${req.id}" has no freshness TTL or schedule`,
});
}
}
}
const dimensionCoverage = computeDimensionCoverage(profile.requirements);
for (const dim of ALL_DIMENSIONS) {
const entry = dimensionCoverage.find((d) => d.dimension === dim);
if (!entry || !entry.hasMainRequired) {
diagnostics.push({
ruleId: "CERT-PROFILE-10",
severity: "error",
message: `dimension "${dim}" lacks a Main required/conditional coverage path`,
});
}
}
if (profile.evaluatorPolicy) {
const ep = profile.evaluatorPolicy;
if (ep.ordinaryEvaluators < 1) {
diagnostics.push({
ruleId: "CERT-PROFILE-11",
severity: "error",
message: "evaluatorPolicy.ordinaryEvaluators must be at least 1",
});
}
if (ep.criticalEvaluators < ep.ordinaryEvaluators) {
diagnostics.push({
ruleId: "CERT-PROFILE-11",
severity: "error",
message: "evaluatorPolicy.criticalEvaluators must be >= ordinaryEvaluators",
});
}
}
return {
valid: diagnostics.length === 0,
diagnostics,
dimensionCoverage,
};
}
function computeDimensionCoverage(requirements: CertificationRequirementV1[]): Array<{
dimension: SiteQualityDimension | "core";
hasMainRequired: boolean;
requirementIds: string[];
}> {
const map = new Map();
for (const req of requirements) {
const existing = map.get(req.dimension) ?? {
hasMainRequired: false,
requirementIds: [],
};
existing.requirementIds.push(req.id);
if (
(req.classification === "required" || req.classification === "conditional") &&
req.gates.includes("promote-main")
) {
existing.hasMainRequired = true;
}
map.set(req.dimension, existing);
}
return Array.from(map.entries()).map(([dimension, val]) => ({
dimension: dimension as SiteQualityDimension | "core",
...val,
}));
}