tools/core/validators/rule.validator.ts

tools/core/validators/rule.validator.ts is a file in Coordination Surface. 114 lines of code and 15 definitions.

import { DECLARATION_KEYS, STAGES } from "../types/rule.types.ts";
import { containsInCode, declaresProperty, reachesRegexLiteral } from "../predicates/source.predicate.ts";
import { findingShapeGaps, isConstructShaped, missingReportIdentity, reportMissing } from "./governance.validator.ts";
import type { Finding } from "../types/segment.types.ts";
import type { RuleContext } from "../types/rule.types.ts";
import { contains } from "../predicates/text.predicate.ts";
import { contractFinding } from "../factories/governance.factory.ts";
import { identityOf } from "../registries/rule.registry.ts";
import { unscopedRead } from "./source.validator.ts";

const REQUIRED_FINDING_FIELDS = ["rule", "path", "locus", "stack", "actual", "expected", "remediation", "healed"];

const matchingFindings = function matchingFindings(path: string, source: string): Finding[] {
    if (!containsInCode(source, "RegExp") && !reachesRegexLiteral(source)) {
        return [];
    }
    return [
        {
            ...contractFinding(
                path,
                "matching",
                "regular expression",
                "match by tree traversal, token comparison or exact string — a hand-written scanner is the mechanism, and the test is the CALL FORM rather than a list of method names, because a name list reopens the moment the language adds a method that accepts a literal",
                "regex banned",
            ),
            rule: "governance/matching",
        },
    ];
};

const reportFindings = function reportFindings(repoRoot: string, path: string): Finding[] {
    const id = identityOf(path);
    const missing = reportMissing(repoRoot, id)
        ? [
              contractFinding(
                  path,
                  id,
                  "no report on disk",
                  `run the pipeline so ${id} emits its report — the report is the re-readable state of a run, and a rule whose findings exist only in the moment of the run is unactionable the instant the process exits`,
                  "report absent",
              ),
          ]
        : [];
    const instance = isConstructShaped(id)
        ? []
        : [
              contractFinding(
                  path,
                  id,
                  "names an instance",
                  "a rule id names the construct it matches, never a path, vendor, filename or threshold — instances live as data the rule cites, so the id reads unchanged when the same shape recurs elsewhere",
                  "literal in id",
              ),
          ];

    return [
        ...missing,
        ...missingReportIdentity(repoRoot, id).map((field) =>
            contractFinding(
                path,
                `${id}.report.${field}`,
                "absent",
                `every report states the run that wrote it — "${field}" is what lets a reader tell an authoritative whole-tree run from a narrowed diagnostic one, and a report that cannot be told apart from a narrowed run is evidence for a claim nobody verified`,
                "report identity",
            ),
        ),
        ...instance,
    ];
};

const declarationFindings = function declarationFindings(repoRoot: string, path: string, source: string): Finding[] {
    const breach = unscopedRead(source);
    const unscoped =
        breach === null ? [] : [contractFinding(path, breach.locus, breach.actual, breach.decide, breach.resolved)];
    const declaresStage = STAGES.some((stage) => contains(source, `"${stage}"`));
    const stage = declaresStage
        ? []
        : [contractFinding(path, "stage", "unrecognized", `stage is one of ${STAGES.join(", ")}`, "unknown stage")];

    return [
        ...unscoped,
        ...DECLARATION_KEYS.filter((key) => !declaresProperty(source, key)).map((key) =>
            contractFinding(
                path,
                key,
                "absent",
                `declare the required field "${key}" — an optional field is one the gate cannot check`,
                "incomplete declaration",
            ),
        ),
        ...reportFindings(repoRoot, path),
        ...stage,
    ];
};

export const sourceFindings = function sourceFindings(repoRoot: string, path: string, source: string): Finding[] {
    const exported = contains(source, "export const rule");
    const contract = exported
        ? declarationFindings(repoRoot, path, source)
        : [
              contractFinding(
                  path,
                  "export",
                  "no exported rule declaration",
                  "export `const rule: RuleDeclaration` so the registry can discover it",
                  "missing export",
              ),
          ];
    return [...matchingFindings(path, source), ...contract];
};

export const shapeFindings = function shapeFindings(context: RuleContext): Finding[] {
    return findingShapeGaps(context.paths, context.read, REQUIRED_FINDING_FIELDS).map((gap) =>
        contractFinding(
            gap.path,
            `finding.${gap.field}`,
            "absent",
            `every emitted finding carries "${gap.field}" — a finding without it is prose the next agent must re-derive`,
            "finding shape",
        ),
    );
};