# tools/rules/governance.rule.ts

> 456 lines of code and 52 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-rules-governance-rule-ts
Source text: https://banes-lab.com/assets/sources/source.d5fc60ce9e87bd4f87f3e769638b34191b3355b55273e16044c5342c452616bd.generated.txt

## Definitions

- `finding` (lexical_declaration, line 101)
- `everyCharacter` (lexical_declaration, line 57)
- `matchingFindings` (lexical_declaration, line 323)
- `isItemKey` (lexical_declaration, line 66)
- `reportFindings` (lexical_declaration, line 341)
- `declarationFindings` (lexical_declaration, line 381)
- `check` (method_definition, line 434, exported)
- `permanentSpan` (lexical_declaration, line 74)
- `shapeFindings` (lexical_declaration, line 421)
- `healOrphans` (lexical_declaration, line 129)
- `writerFindings` (lexical_declaration, line 159)
- `healChannels` (lexical_declaration, line 188)
- `unevaluableFindings` (lexical_declaration, line 253)
- `unrepairableFindings` (lexical_declaration, line 296)
- `sourceFindings` (lexical_declaration, line 405)
- `REQUIRED_FINDING_FIELDS` (lexical_declaration, line 31)
- `CORE` (lexical_declaration, line 33)
- `RUN_ENTRIES` (lexical_declaration, line 35)
- `SANCTIONED_WRITERS` (lexical_declaration, line 37)
- `ITEM_KEY_SEPARATOR` (lexical_declaration, line 47)
- `isUpperLetter` (lexical_declaration, line 49)
- `isDigit` (lexical_declaration, line 53)
- `at` (lexical_declaration, line 67)
- `RULE_SELECTOR` (lexical_declaration, line 81)
- `STAGE_SELECTOR` (lexical_declaration, line 83)
- `channelScopeResolver` (lexical_declaration, line 85)
- `Healing` (interface_declaration, line 124)
- `known` (lexical_declaration, line 160)
- `stale` (lexical_declaration, line 189)
- `UNUSABLE` (lexical_declaration, line 218)
- `SCOPE_GAP_DECIDE` (lexical_declaration, line 220)
- `ScopeGap` (type_alias_declaration, line 223)
- `scopeGapFinding` (lexical_declaration, line 225)
- `unusable` (lexical_declaration, line 226)
- `UNREPAIRABLE_DECIDE` (lexical_declaration, line 277)
- `id` (lexical_declaration, line 342)
- `missing` (lexical_declaration, line 343)
- `instance` (lexical_declaration, line 354)
- `breach` (lexical_declaration, line 382)
- `unscoped` (lexical_declaration, line 383)
- `declaresStage` (lexical_declaration, line 384)
- `stage` (lexical_declaration, line 385)
- `exported` (lexical_declaration, line 406)
- `contract` (lexical_declaration, line 407)
- `rule` (lexical_declaration, line 433, exported)
- `{ repoRoot }` (lexical_declaration, line 435, exported)
- `claimed` (lexical_declaration, line 436, exported)
- `orphans` (lexical_declaration, line 437, exported)
- `writers` (lexical_declaration, line 438, exported)
- `channels` (lexical_declaration, line 439, exported)
- `sources` (lexical_declaration, line 440, exported)
- `findings` (lexical_declaration, line 442, exported)

## Uses

- [config/surface.config.ts](https://banes-lab.com/source/coordination/config/surface.config.ts.md)
- [tools/core/predicates/source.predicate.ts](https://banes-lab.com/source/coordination/tools/core/predicates/source.predicate.ts.md)
- [tools/core/predicates/text.predicate.ts](https://banes-lab.com/source/coordination/tools/core/predicates/text.predicate.ts.md)
- [tools/core/runners/fixture.runner.ts](https://banes-lab.com/source/coordination/tools/core/runners/fixture.runner.ts.md)
- [tools/core/validators/channel.validator.ts](https://banes-lab.com/source/coordination/tools/core/validators/channel.validator.ts.md)
- [tools/core/validators/governance.validator.ts](https://banes-lab.com/source/coordination/tools/core/validators/governance.validator.ts.md)
- [tools/core/validators/writer.validator.ts](https://banes-lab.com/source/coordination/tools/core/validators/writer.validator.ts.md)

## Source

```typescript
import { DECLARATION_KEYS, STAGES } from "../core/types/rule.types.ts";
import type { RuleContext, RuleDeclaration, RuleResult } from "../core/types/rule.types.ts";

import { basename, resolve } from "node:path";
import {
    claimedReportIds,
    deleteReport,
    findingShapeGaps,
    isConstructShaped,
    missingReportIdentity,
    orphanReports,
    reportMissing,
    selfAuditedReports,
    unaccountedScopeGaps,
    unevaluableScopes,
    unrepairableLoci,
    withdrawnStandings,
} from "../core/validators/governance.validator.ts";
import { containsInCode, declaresProperty, reachesRegexLiteral } from "../core/predicates/source.predicate.ts";
import { contentIsUnrepairable, lifetimeOf, surfacePath } from "../../config/surface.config.ts";
import { removeChannel, staleChannels } from "../core/validators/channel.validator.ts";
import type { Finding } from "../core/types/segment.types.ts";
import { GENERATED_DIR } from "../core/constants/path.constants.ts";
import { RULE_ROOT } from "../core/constants/layer.constants.ts";
import { contains } from "../core/predicates/text.predicate.ts";
import { existsSync } from "node:fs";
import { identityOf } from "../core/registries/rule.registry.ts";
import { unsanctionedWriters } from "../core/validators/writer.validator.ts";
import { unscopedRead } from "../core/validators/source.validator.ts";

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

const CORE = surfacePath("core");

const RUN_ENTRIES = [`${surfacePath("entrypoints")}/pipeline.entrypoint.ts`];

const SANCTIONED_WRITERS = [
    `${CORE}/writers/repair.writer.ts`,
    `${CORE}/reporters/rule.reporter.ts`,
    `${CORE}/reporters/segment.reporter.ts`,
    `${CORE}/registries/claim.registry.ts`,
    `${CORE}/registries/snapshot.registry.ts`,
    `${CORE}/validators/channel.validator.ts`,
    `${CORE}/validators/governance.validator.ts`,
];

const ITEM_KEY_SEPARATOR = "-";

const isUpperLetter = function isUpperLetter(char: string): boolean {
    return char >= "A" && char <= "Z";
};

const isDigit = function isDigit(char: string): boolean {
    return char >= "0" && char <= "9";
};

const everyCharacter = function everyCharacter(text: string, accepts: (char: string) => boolean): boolean {
    for (const char of text) {
        if (!accepts(char)) {
            return false;
        }
    }
    return true;
};

const isItemKey = function isItemKey(locus: string): boolean {
    const at = locus.indexOf(ITEM_KEY_SEPARATOR);
    if (at <= 0 || at === locus.length - 1) {
        return false;
    }
    return everyCharacter(locus.slice(0, at), isUpperLetter) && everyCharacter(locus.slice(at + 1), isDigit);
};

const permanentSpan = function permanentSpan(reported: string, locus: string): boolean {
    if (!isItemKey(locus)) {
        return false;
    }
    return lifetimeOf(reported)?.retention === "accumulating";
};

const RULE_SELECTOR = "rule=";

const STAGE_SELECTOR = "stage=";

const channelScopeResolver = function channelScopeResolver(
    repoRoot: string,
    claimed: ReadonlySet<string>,
): (scope: string) => boolean {
    return (scope: string): boolean => {
        if (scope.startsWith(RULE_SELECTOR)) {
            return claimed.has(scope.slice(RULE_SELECTOR.length));
        }
        if (scope.startsWith(STAGE_SELECTOR)) {
            return STAGES.some((stage) => stage === scope.slice(STAGE_SELECTOR.length));
        }

        return existsSync(resolve(repoRoot, scope));
    };
};

const finding = function finding(
    path: string,
    locus: string,
    actual: string,
    decide: string,
    resolved: string,
): Finding {
    return {
        actual,
        expected: null,
        healed: false,
        line: 0,
        locus,
        path,
        remediation: { action: "declare", decide, deterministic: false, from: actual, target: path, to: null },
        rule: "governance/ruleContract",
        stack: [
            { check: "ruleSource", resolved: basename(path) },
            { check: "contract", resolved },
        ],
    };
};

interface Healing {
    readonly findings: Finding[];
    readonly healed: string[];
}

const healOrphans = function healOrphans(repoRoot: string, claimed: ReadonlySet<string>): Healing {
    const orphans = orphanReports(repoRoot, claimed);
    for (const name of orphans) {
        deleteReport(repoRoot, name);
    }

    const findings: Finding[] = orphans.map((name) => ({
        actual: name,
        expected: null,
        healed: true,
        line: 0,
        locus: name,
        path: `${GENERATED_DIR}/${name}`,
        remediation: {
            action: "delete",
            decide: "",
            deterministic: true,
            from: name,
            target: `${GENERATED_DIR}/${name}`,
            to: null,
        },
        rule: "governance/orphanReport",
        stack: [
            { check: "reportShape", resolved: "declares rule and stage" },
            { check: "claimedBy", resolved: "no rule declaration and no step emission" },
        ],
    }));
    return { findings, healed: [...orphans] };
};

const writerFindings = function writerFindings(context: RuleContext): Finding[] {
    const known = new Set(context.paths.filter((path) => path.endsWith(".ts")));
    return unsanctionedWriters(RUN_ENTRIES, known, context.read, SANCTIONED_WRITERS).map((breach) => ({
        actual: `${breach.path} writes the filesystem directly and a run reaches it`,
        expected: null,
        healed: false,
        line: 0,
        locus: breach.member,
        path: breach.path,
        remediation: {
            action: "declare",
            decide:
                "route the write through the restricted writer, which takes the scope this run DECLARED and " +
                "refuses a path outside it — so containment is answered at one function rather than by a witness " +
                "nobody holds for a repair scattered across source. A gate over a DIRECTORY reports a complete " +
                "funnel while a module writes beneath it, which is why this ranges over what a run REACHES",
            deterministic: false,
            from: breach.member,
            target: breach.path,
            to: null,
        },
        rule: "governance/unsanctionedWriter",
        stack: [
            { check: "reachableFromARun", resolved: "yes" },
            { check: "sanctionedWriter", resolved: "no" },
        ],
    }));
};

const healChannels = function healChannels(repoRoot: string, claimed: ReadonlySet<string>): Healing {
    const stale = staleChannels(repoRoot, channelScopeResolver(repoRoot, claimed));
    for (const channel of stale) {
        removeChannel(repoRoot, channel.name);
    }

    const findings: Finding[] = stale.map((channel) => ({
        actual: channel.scope,
        expected: null,
        healed: true,
        line: 0,
        locus: channel.scope,
        path: `${GENERATED_DIR}/${channel.name}`,
        remediation: {
            action: "delete",
            decide: "",
            deterministic: true,
            from: channel.name,
            target: `${GENERATED_DIR}/${channel.name}`,
            to: null,
        },
        rule: "governance/staleChannel",
        stack: [
            { check: "channelShape", resolved: "names its scope and declares itself non-authoritative" },
            { check: "scopeResolves", resolved: "no" },
        ],
    }));
    return { findings, healed: stale.map((channel) => channel.name) };
};

const UNUSABLE = -1;

const SCOPE_GAP_DECIDE =
    "two counts in one report disagreeing means the check narrowed its own scope, and a narrowing nobody named is a blind spot rather than a decision. A count is evidence of coverage only over the surface the scan reached, so a report whose reached set is smaller than the set it was handed names every path it skipped and the class it skipped it under — then the arithmetic closes and a reader can audit the green. This axis reads REPORTS rather than rules, so it fires on any check that silently narrows, including the ones nobody has thought to suspect. AND THE TWO OPERANDS MUST BE COMMENSURABLE OR THE SUBTRACTION MEANS NOTHING: `reached` is unit-bearing and names members OF THE HANDED SET, so a check whose population is not the handed files — one walking rules, declarations or slots — publishes that population under a key naming its own unit and declares NO reached set. Claiming one in a foreign unit satisfies the disclosure and breaks the audit, which is a more expensive failure than the silence it replaces. AND A REACHED OPERAND THAT NAMES NO MEMBERS IS SKIPPED RATHER THAN COMPARED: a count answers how many and never WHICH, so nothing can be audited against it and the axis passes over the report in silence while the field reads as evidence — publish the members, or publish nothing under that key";

type ScopeGap = ReturnType<typeof unaccountedScopeGaps>[number];

const scopeGapFinding = function scopeGapFinding(gap: ScopeGap): Finding {
    const unusable = gap.reached === UNUSABLE;
    return {
        actual: unusable
            ? "this report declares a reached operand that names no members, so the arithmetic skips it entirely"
            : `${String(gap.handed - gap.reached - gap.named)} file(s) were handed to this check, not reached, and named in no skip list`,
        expected: null,
        healed: false,
        line: 0,
        locus: gap.report,
        path: `${GENERATED_DIR}/${gap.report}`,
        remediation: {
            action: "declare",
            decide: SCOPE_GAP_DECIDE,
            deterministic: false,
            from: gap.report,
            target: `${GENERATED_DIR}/${gap.report}`,
            to: null,
        },
        rule: unusable ? "governance/unusableOperand" : "governance/undeclaredExclusion",
        stack: [
            { check: "handed", resolved: String(gap.handed) },
            { check: "reached", resolved: unusable ? "declared, not enumerable" : String(gap.reached) },
            { check: "namedSkips", resolved: String(gap.named) },
        ],
    };
};

const unevaluableFindings = function unevaluableFindings(repoRoot: string): Finding[] {
    return unevaluableScopes(repoRoot).map((gap) => ({
        actual: `${String(gap.handed)} were handed to this check and it publishes no population at all`,
        expected: null,
        healed: false,
        line: 0,
        locus: gap.report,
        path: `${GENERATED_DIR}/${gap.report}`,
        remediation: {
            action: "declare",
            decide: "a comparison cannot be wrong while one of its operands does not exist, so a report publishing NO population sits in neither pass nor fail on the scope axis — it is UNEVALUABLE, and unevaluable reads exactly like covered because both are silent. The handed count alone is inherited from whatever handed it and asserts nothing the check itself measured. Publish the population the check actually walked: as `reached` where its members ARE the handed set, or under a key naming its own unit where they are not",
            deterministic: false,
            from: gap.report,
            target: `${GENERATED_DIR}/${gap.report}`,
            to: null,
        },
        rule: "governance/unevaluableScope",
        stack: [
            { check: "handed", resolved: String(gap.handed) },
            { check: "population", resolved: "none published" },
        ],
    }));
};

const UNREPAIRABLE_DECIDE =
    "A FINDING'S LOCUS IS CONSUMED AS ITS REPAIR TARGET, and a surface whose declared mutability " +
    "is frozen cannot receive one — so the finding names a repair nobody may perform. This is " +
    "worse than a refusal rather than equivalent to it: a refusal produces a party who knows they " +
    "are blocked, while a frozen target that is nonetheless writable produces a party who edits, " +
    "is told the edit succeeded, watches the finding clear, and loses the repair at the next " +
    "regeneration — a party who believes they are done. Name the SOURCE the target is derived " +
    "from, where one exists, so the locus points where the repair belongs; withdraw the finding " +
    "where no source exists, because a report nobody can drain teaches every reader to discount " +
    "the color and the cost lands on the findings beside it. THE LIFETIME IS DECLARED AND THE " +
    "JOIN IS BETWEEN TWO OPERANDS THAT ALREADY EXIST, so this reads unchanged on the next surface " +
    "class the configuration declares and needs no walk to learn a skip. AND THE SURFACE'S ANSWER IS " +
    "THE WRONG OPERAND FOR A FINDING ANCHORED TO A SPAN: an accumulating surface ACCEPTS a write, so " +
    "restating a claim by appending is performable and the surface reads as repairable — while the " +
    "SPAN the finding names is permanent by the same lifetime that makes the surface appendable, so " +
    "the original span still carries the defect and the finding still stands whatever anybody " +
    "appends. The repair is reachable and the finding is unclearable, which are different properties, " +
    "and only the span's mutability separates them";

const unrepairableFindings = function unrepairableFindings(repoRoot: string): Finding[] {
    return unrepairableLoci(repoRoot, contentIsUnrepairable, permanentSpan).map((locus) => ({
        actual:
            locus.reason === "frozenTarget"
                ? `${locus.rule} names a repair target whose declared mutability forbids the repair it computes`
                : `${locus.rule} anchors to a span that is permanent by the declared retention of the surface holding it, so the surface accepts a write and the span the finding names does not`,
        expected: null,
        healed: false,
        line: 0,
        locus: locus.target,
        path: `${GENERATED_DIR}/${locus.report}`,
        remediation: {
            action: "declare",
            decide: UNREPAIRABLE_DECIDE,
            deterministic: false,
            from: locus.target,
            target: `${GENERATED_DIR}/${locus.report}`,
            to: null,
        },
        rule: "governance/unrepairableLocus",
        stack: [
            { check: "reportedBy", resolved: locus.rule },
            { check: "unrepairableBy", resolved: locus.reason },
        ],
    }));
};

const matchingFindings = function matchingFindings(path: string, source: string): Finding[] {
    if (!containsInCode(source, "RegExp") && !reachesRegexLiteral(source)) {
        return [];
    }
    return [
        {
            ...finding(
                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)
        ? [
              finding(
                  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)
        ? []
        : [
              finding(
                  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) =>
            finding(
                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 ? [] : [finding(path, breach.locus, breach.actual, breach.decide, breach.resolved)];
    const declaresStage = STAGES.some((stage) => contains(source, `"${stage}"`));
    const stage = declaresStage
        ? []
        : [finding(path, "stage", "unrecognised", `stage is one of ${STAGES.join(", ")}`, "unknown stage")];

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

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)
        : [
              finding(
                  path,
                  "export",
                  "no exported rule declaration",
                  "export `const rule: RuleDeclaration` so the registry can discover it",
                  "missing export",
              ),
          ];
    return [...matchingFindings(path, source), ...contract];
};

const shapeFindings = function shapeFindings(context: RuleContext): Finding[] {
    return findingShapeGaps(context.paths, context.read, REQUIRED_FINDING_FIELDS).map((gap) =>
        finding(
            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",
        ),
    );
};

export const rule: RuleDeclaration = {
    check(context: RuleContext): RuleResult {
        const { repoRoot } = context;
        const claimed = claimedReportIds(repoRoot);
        const orphans = healOrphans(repoRoot, claimed);
        const writers = writerFindings(context);
        const channels = healChannels(repoRoot, claimed);
        const sources = context.paths.filter((path) => contains(path, RULE_ROOT));

        const findings = [
            ...orphans.findings,
            ...writers,
            ...channels.findings,
            ...unaccountedScopeGaps(repoRoot).map(scopeGapFinding),
            ...unevaluableFindings(repoRoot),
            ...unrepairableFindings(repoRoot),
            ...sources.flatMap((path) => sourceFindings(repoRoot, path, context.read(path))),
            ...shapeFindings(context),
        ];

        return {
            derivations: {
                identityContract:
                    "a check's identity is DERIVED from its filename subject and is declared nowhere, so the two-name divergence this walk once compared is unconstructible rather than absent. The comparison is not reported as passing, because a check whose members cannot exist is dead rather than green — what replaces it is the registry refusing any declaration that carries an id at all, which is the collapse the join was a substitute for",
                reportsAudited: [...claimed].toSorted((left, right) => left.localeCompare(right, "en")),
                ruleSourcesWalked: sources,
                selfAuditStanding:
                    "this check reads reports its own run writes, so the verdict on any report listed under selfAudited describes the PREVIOUS run of it — a first verdict after repairing this check is one run behind, and the second run is the one authoritative for that member. The lag is stated rather than excluded, because excluding a check from its own audit installs a blind spot shaped exactly like the thing being hidden",
                selfAudited: selfAuditedReports(repoRoot, context.id),
                skippedAsNotARuleSource: context.paths.filter((path) => !contains(path, RULE_ROOT)),
                standingContract:
                    "a verdict carries a STANDING beside its value, and a report listed under withdrawnStandings read a surface that has CHANGED since the report was written — so that verdict describes a tree that has moved and is not authoritative to quote, while the verdict itself is untouched because declaring it a failure would assert a defect nothing observed. This is published as a DERIVATION rather than emitted as a finding, deliberately: on a surface several parties write continuously every report is stale within moments of any write, so a finding here would be red between every run and a permanent red teaches every reader to discount the color — the cost landing on the findings beside it rather than on itself. The reader who needs this is the one about to QUOTE a verdict, and the honest mechanism hands them the standing rather than failing a build over a transient",
                withdrawnStandings: withdrawnStandings(repoRoot),
            },
            findings,
            healed: [...orphans.healed, ...channels.healed],
        };
    },
    extensions: [".ts"],
    heals: true,
    invariant:
        "every rule source declares the full contract and emits findings in the machine-actionable shape, and every report on disk is claimed by something that emits it",
    jurisdiction: "taxonomy",
    kinds: [
        "ruleContract",
        "declarationContract",
        "orphanReport",
        "unsanctionedWriter",
        "staleChannel",
        "unevaluableScope",
        "unrepairableLocus",
        "matching",
    ],
    readsTree:
        "a channel names the SCOPE its run declared, and whether that scope still resolves is a question about " +
        "the TREE rather than about any file's contents — a scope naming a subtree is answered by whether the " +
        "subtree is there, which no set of scanned file contents can decide. The reports this walk reads are " +
        "generated output and sit outside the governed path set by construction, so a rule reading only that " +
        "set would report a clean green over every report and every channel in the directory the pipeline " +
        "writes",

    stage: "meta",

    wholeScopeOnly: true,
};
```
