tools/core/validators/record.validator.ts

tools/core/validators/record.validator.ts is a file in Coordination Surface. 171 lines of code and 24 definitions.

import type { Finding } from "../types/segment.types.ts";
import type { RecordEntry } from "../types/record.types.ts";

export const REQUIRED = ["type", "statement", "confidence", "source"];

export const TENSION_REQUIRED = [
    "kind",
    "conflicts",
    "with",
    "axis",
    "cause",
    "resolution",
    "remediate",
    "compatible",
    "incompatible",
];

export const KINDS = ["apparent", "real"];

export const CONFIDENCE_TIERS = ["verified-local", "vendor-doc", "community", "inferred"];

export const DERIVATIONS = ["original", "adapted", "quoted"];

export const DISPUTED = "disputed";

export const recordFinding = function recordFinding(
    kind: string,
    path: string,
    line: number,
    locus: string,
    actual: string,
    decide: string,
): Finding {
    return {
        actual,
        expected: null,
        healed: false,
        line,
        locus,
        path,
        remediation: { action: "declare", decide, deterministic: false, from: locus, target: path, to: null },
        rule: `record/${kind}`,
        stack: [
            { check: "record", resolved: locus },
            { check: kind, resolved: "failed" },
        ],
    };
};

const NO_INCOMPATIBILITY = "none";

const outsideVocabulary = function outsideVocabulary(
    value: string | undefined,
    vocabulary: readonly string[],
): value is string {
    return value !== undefined && !vocabulary.includes(value);
};

const missingKeys = function missingKeys(
    path: string,
    record: RecordEntry,
    required: readonly string[],
    kind: string,
    explain: (key: string) => readonly [string, string],
): Finding[] {
    return required
        .filter((key) => record.keys[key] === undefined)
        .map((key) => {
            const [actual, decide] = explain(key);
            return recordFinding(kind, path, record.line, record.id, actual, decide);
        });
};

const tierFindings = function tierFindings(path: string, record: RecordEntry): Finding[] {
    const { confidence } = record.keys;
    return outsideVocabulary(confidence, CONFIDENCE_TIERS)
        ? [
              recordFinding(
                  "unknownTier",
                  path,
                  record.line,
                  record.id,
                  `${record.id} declares confidence "${confidence}"`,
                  `use a declared tier — ${CONFIDENCE_TIERS.join(", ")} — because precedence between evidence is decided by the tier, and a tier outside the vocabulary cannot be ranked against another. HOW a record came to be is a DIFFERENT axis and belongs in derivation — ${DERIVATIONS.join(", ")} — since material adapted from another source can be strong or weak evidence independently, so a word describing provenance cannot be ranked against one describing strength and pushing it into this vocabulary would leave the whole set unrankable`,
              ),
          ]
        : [];
};

const derivationFindings = function derivationFindings(path: string, record: RecordEntry): Finding[] {
    const { derivation } = record.keys;
    return outsideVocabulary(derivation, DERIVATIONS)
        ? [
              recordFinding(
                  "unknownDerivation",
                  path,
                  record.line,
                  record.id,
                  `${record.id} declares derivation "${derivation}"`,
                  `use a declared derivation — ${DERIVATIONS.join(", ")} — because an open provenance vocabulary reads as governed while resolving to nothing, exactly as an open tier would. The field is OPTIONAL and states how the record came to be; its strength stays in confidence`,
              ),
          ]
        : [];
};

const disputedFindings = function disputedFindings(path: string, record: RecordEntry): Finding[] {
    const surfaced = record.keys["see"] !== undefined || record.keys["detect"] !== undefined;
    return record.keys["status"] === DISPUTED && !surfaced
        ? [
              recordFinding(
                  "disputedNotSurfaced",
                  path,
                  record.line,
                  record.id,
                  `${record.id} is disputed but neither links a counterpart nor says how to resolve it`,
                  "a conflict is surfaced rather than silently resolved — either see the record that disagrees, or state in detect how a reader settles it; a disputed record carrying neither says something disagrees without saying what or how",
              ),
          ]
        : [];
};

export const checkSchema = function checkSchema(path: string, record: RecordEntry): Finding[] {
    return [
        ...missingKeys(path, record, REQUIRED, "missingKey", (key) => [
            `${record.id} has no ${key}`,
            `add ${key} — the contract requires it on every typed record, and a record missing one cannot be evaluated`,
        ]),
        ...tierFindings(path, record),
        ...derivationFindings(path, record),
        ...disputedFindings(path, record),
    ];
};

const kindFindings = function kindFindings(path: string, record: RecordEntry): Finding[] {
    const { kind } = record.keys;
    return outsideVocabulary(kind, KINDS)
        ? [
              recordFinding(
                  "unknownKind",
                  path,
                  record.line,
                  record.id,
                  `kind is "${kind}"`,
                  "kind is apparent or real — apparent means both principles survive once their domains separate, real means one must be chosen",
              ),
          ]
        : [];
};

const contradictionFindings = function contradictionFindings(path: string, record: RecordEntry): Finding[] {
    const { incompatible, kind } = record.keys;
    const namesIncompatibility = incompatible !== undefined && incompatible !== NO_INCOMPATIBILITY;

    if (kind === "apparent" && namesIncompatibility) {
        return [
            recordFinding(
                "kindContradiction",
                path,
                record.line,
                record.id,
                `kind is apparent while incompatible names "${incompatible}"`,
                "an apparent tension resolves by separating domains, so nothing is incompatible; set incompatible to none, or the tension is real",
            ),
        ];
    }

    return kind === "real" && incompatible === NO_INCOMPATIBILITY
        ? [
              recordFinding(
                  "kindContradiction",
                  path,
                  record.line,
                  record.id,
                  "kind is real while incompatible is none",
                  "a real tension is a genuine trade-off, so name what cannot hold together; if nothing cannot, the tension is apparent",
              ),
          ]
        : [];
};

export const checkTension = function checkTension(path: string, record: RecordEntry): Finding[] {
    return [
        ...missingKeys(path, record, TENSION_REQUIRED, "missingTensionKey", (key) => [
            `${record.id} is a tension and has no ${key}`,
            `add ${key} — a tension is a typed relation, and without every key it degrades to prose that cannot be resolved`,
        ]),
        ...kindFindings(path, record),
        ...contradictionFindings(path, record),
    ];
};