# tools/rules/declaration.rule.ts

> 319 lines of code and 40 definitions.

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

## Definitions

- `finding` (lexical_declaration, line 38)
- `leftoverFinding` (lexical_declaration, line 239)
- `readsOf` (lexical_declaration, line 183)
- `inSurfaceRoot` (lexical_declaration, line 213)
- `placeholderSources` (lexical_declaration, line 222)
- `placeholderFindings` (lexical_declaration, line 264)
- `rootFindings` (lexical_declaration, line 57)
- `containerFindings` (lexical_declaration, line 82)
- `artifactFindings` (lexical_declaration, line 148)
- `axisFinding` (lexical_declaration, line 187)
- `AXIS_ADAPTER` (lexical_declaration, line 6)
- `AXIS_GOVERNED` (lexical_declaration, line 8)
- `AXIS_REACHES` (lexical_declaration, line 13)
- `SSOT` (lexical_declaration, line 36)
- `Taxonomy` (type_alias_declaration, line 55)
- `declaredFolders` (lexical_declaration, line 83)
- `missingFolders` (lexical_declaration, line 87)
- `missingCorpora` (lexical_declaration, line 91)
- `foreignFindings` (lexical_declaration, line 115)
- `marker` (lexical_declaration, line 117)
- `Consumer` (type_alias_declaration, line 181)
- `reads` (lexical_declaration, line 188)
- `Scanned` (interface_declaration, line 217)
- `treeRoot` (lexical_declaration, line 223)
- `treeFiles` (lexical_declaration, line 224)
- `manifest` (lexical_declaration, line 225)
- `onDisk` (lexical_declaration, line 226)
- `handed` (lexical_declaration, line 230)
- `seen` (lexical_declaration, line 233)
- `rule` (lexical_declaration, line 273, exported)
- `check` (method_definition, line 274, exported)
- `data` (lexical_declaration, line 275, exported)
- `{ repoRoot }` (lexical_declaration, line 276, exported)
- `roots` (lexical_declaration, line 278, exported)
- `upstream` (lexical_declaration, line 285, exported)
- `resolvedRoots` (lexical_declaration, line 286, exported)
- `resolvedUpstream` (lexical_declaration, line 287, exported)
- `consumers` (lexical_declaration, line 289, exported)
- `axisInspected` (lexical_declaration, line 292, exported)
- `findings` (lexical_declaration, line 297, exported)

## Uses

- [config/surface.config.ts](https://banes-lab.com/source/coordination/config/surface.config.ts.md)
- [tools/core/resolvers/artifact.resolver.ts](https://banes-lab.com/source/coordination/tools/core/resolvers/artifact.resolver.ts.md)

## Source

```typescript
import type { RuleContext, RuleDeclaration, RuleResult } from "../core/types/rule.types.ts";
import { axisConsumers, silentOnItsOwnAxis } from "../core/validators/declaration.validator.ts";
import { foreignMarkerIn, isDirectory } from "../core/inspectors/taxonomy.inspector.ts";
import { slotText, surfacePath, surfacePrefix } from "../../config/surface.config.ts";

const AXIS_ADAPTER = slotText("project", "runtime_adapter");

const AXIS_GOVERNED = [
    `${surfacePath("pipeline")}/`,
    surfacePrefix().length === 0 ? AXIS_ADAPTER : `${surfacePrefix()}/${AXIS_ADAPTER}`,
];

const AXIS_REACHES = [
    "lifetimeOf(",
    "seededLifetimeOf(",
    "lifetime.declared",
    "declared.retention",
    "declared.mutability",
    "declared.removal",
];
import {
    BEHAVIOUR_TREE_PLACEHOLDER,
    BOOTSTRAP_DOCUMENT,
    PACKAGE_MANIFEST,
    SURFACE_ROOT,
    UPSTREAM_ROOTS,
} from "../core/constants/path.constants.ts";
import { existsSync, readFileSync } from "node:fs";
import { toPosix, walk } from "../core/iterators/file.iterator.ts";
import type { Finding } from "../core/types/segment.types.ts";
import { resolve } from "node:path";
import { inspectManifests } from "../core/inspectors/manifest.inspector.ts";
import { resolveArtifactRoots } from "../core/resolvers/artifact.resolver.ts";
import { underRoots } from "../core/filters/scope.filter.ts";

const SSOT = surfacePath("taxonomy_config");

const finding = function finding(kind: string, path: string, locus: string, decide: string): Finding {
    return {
        actual: `${path} is declared but absent`,
        expected: null,
        healed: false,
        line: 0,
        locus,
        path: SSOT,
        remediation: { action: "declare", decide, deterministic: false, from: path, target: SSOT, to: null },
        rule: `declaration/${kind}`,
        stack: [
            { check: "declared", resolved: locus },
            { check: "onDisk", resolved: "absent" },
        ],
    };
};

type Taxonomy = RuleContext["taxonomy"];

const rootFindings = function rootFindings(repoRoot: string, roots: readonly string[], upstream: readonly string[]): Finding[] {
    return [
        ...roots
            .filter((root) => !isDirectory(repoRoot, root))
            .map((root) =>
                finding(
                    "missingRoot",
                    root,
                    `root "${root}"`,
                    "create the root, or remove it from containers / specialContainers / corpusRoots — a root that does not exist governs nothing",
                ),
            ),
        ...upstream
            .filter((root) => !isDirectory(repoRoot, root))
            .map((root) =>
                finding(
                    "missingUpstreamRoot",
                    root,
                    `upstream root "${root}"`,
                    "create the tree, or remove it from the upstream declaration — an upstream root EXEMPTS its contents from the naming, tense and reference gates, so one that resolves to nothing exempts nothing while reading as a considered exclusion, and the next tree that lands under a similar name inherits an exemption nobody granted it",
                ),
            ),
    ];
};

const containerFindings = function containerFindings(repoRoot: string, data: Taxonomy): Finding[] {
    const declaredFolders: [string, readonly string[]][] = [
        ...Object.entries(data.containers),
        ...Object.entries(data.specialContainers),
    ];
    const missingFolders = declaredFolders
        .filter(([root]) => isDirectory(repoRoot, root))
        .flatMap(([root, folders]) => folders.map((folder) => ({ folder, root })))
        .filter(({ folder, root }) => !isDirectory(repoRoot, `${root}/${folder}`));
    const missingCorpora = Object.entries(data.corpusRoots).filter(
        ([root, config]) => !isDirectory(repoRoot, `${root}/${config.filedUnder}`),
    );

    return [
        ...missingFolders.map(({ folder, root }) =>
            finding(
                "missingContainer",
                `${root}/${folder}`,
                `"${folder}" under "${root}"`,
                "create the folder, or drop it from the root's declared set — an undeclared-but-present folder fails placement, while a declared-but-absent one fails nothing at all",
            ),
        ),
        ...missingCorpora.map(([root, config]) =>
            finding(
                "missingCorpusSubtree",
                `${root}/${config.filedUnder}`,
                `filedUnder "${config.filedUnder}" of corpus root "${root}"`,
                "create the subtree, or correct filedUnder — the facet filing check is skipped entirely when it resolves nowhere",
            ),
        ),
    ];
};

const foreignFindings = function foreignFindings(repoRoot: string, roots: readonly string[], data: Taxonomy): Finding[] {
    return roots.flatMap((root) => {
        const marker = isDirectory(repoRoot, root) ? foreignMarkerIn(repoRoot, root, data) : null;
        if (marker === null) {
            return [];
        }
        return [
            {
                actual: `${root} is declared, and holds ${marker.evidence}`,
                expected: null,
                healed: false,
                line: 0,
                locus: `root "${root}"`,
                path: SSOT,
                remediation: {
                    action: "declare",
                    decide: `${marker.why} Remove the declaration — a tree whose names are load-bearing elsewhere is governed on its own axis, never renamed to satisfy this grammar.`,
                    deterministic: false,
                    from: root,
                    target: SSOT,
                    to: null,
                },
                rule: "declaration/foreignGrammarClaimed",
                stack: [
                    { check: "declared", resolved: `root "${root}"` },
                    { check: "onDisk", resolved: "present" },
                    { check: "foreignGrammar", resolved: marker.evidence },
                ],
            },
        ];
    });
};

const artifactFindings = function artifactFindings(repoRoot: string, data: Taxonomy): Finding[] {
    return resolveArtifactRoots(repoRoot, data).flatMap((root) =>
        root.unresolved === null
            ? []
            : [
                  {
                      actual: root.unresolved,
                      expected: null,
                      healed: false,
                      line: 0,
                      locus: `artifactRoots "${root.key}"`,
                      path: SSOT,
                      remediation: {
                          action: "declare",
                          decide:
                              "repair the binding, or drop the artifact root — every content rule declaring this " +
                              "jurisdiction currently scans nothing and reports a pass for files it never opened",
                          deterministic: false,
                          from: root.key,
                          target: SSOT,
                          to: null,
                      },
                      rule: "declaration/unresolvedArtifactRoot",
                      stack: [
                          { check: "binding", resolved: root.binding },
                          { check: "field", resolved: root.field },
                          { check: "resolved", resolved: "absent" },
                      ],
                  },
              ],
    );
};

type Consumer = ReturnType<typeof axisConsumers>[number];

const readsOf = function readsOf(consumer: Consumer, separator: string, empty: string): string {
    return consumer.read.length === 0 ? empty : consumer.read.join(separator);
};

const axisFinding = function axisFinding(path: string, consumer: Consumer): Finding {
    const reads = readsOf(consumer, "+", "no axis");
    return {
        actual: `${consumer.name} names the ${consumer.asserted} axis and never reads it, deciding from ${readsOf(consumer, " and ", "no declared axis")} instead`,
        expected: `${consumer.name} reads the ${consumer.asserted} field its own identity asserts`,
        healed: false,
        line: consumer.line,
        locus: consumer.name,
        path,
        remediation: {
            action: "declare",
            decide: "the declared axes are INDEPENDENT by the model this tree governs itself by — no two are derivable from each other — so a predicate deciding one of them from the others takes a derivation the model states is unavailable, and it is wrong from its first line rather than by drifting. THE SCOPE IS A BODY SCAN RATHER THAN A SIGNATURE SCAN, DELIBERATELY: narrowing to functions whose PARAMETER TYPE is the declared record keys on the property the CORRECT members share, so it passes every true negative and never sees a predicate that resolves the record inside its own body, which is the shape this check exists for. The residue is a predicate naming NO axis while reading the wrong fields, invisible here and stated rather than gated weakly, because the general question is what a predicate is FOR and no surface holds that",
            deterministic: false,
            from: reads,
            target: path,
            to: consumer.asserted,
        },
        rule: "declaration/unreadAssertedAxis",
        stack: [
            { check: "reachesLifetime", resolved: "yes" },
            { check: "asserts", resolved: consumer.asserted },
            { check: "reads", resolved: reads },
        ],
    };
};

const inSurfaceRoot = function inSurfaceRoot(path: string): string {
    return SURFACE_ROOT.length === 0 ? path : `${SURFACE_ROOT}/${path}`;
};

interface Scanned {
    readonly path: string;
    readonly text: string;
}

const placeholderSources = function placeholderSources(context: RuleContext): Scanned[] {
    const treeRoot = resolve(context.repoRoot, surfacePath("behaviour_tree"));
    const treeFiles = existsSync(treeRoot) ? walk({ extensions: [".md"], ignored: [], root: treeRoot }) : [];
    const manifest = resolve(context.repoRoot, inSurfaceRoot(PACKAGE_MANIFEST));
    const onDisk = [...treeFiles, ...(existsSync(manifest) ? [manifest] : [])].map((absolute) => ({
        path: toPosix(context.repoRoot, absolute),
        text: readFileSync(absolute, "utf8"),
    }));
    const handed = context.paths
        .filter((path) => path.endsWith(".md"))
        .map((path) => ({ path, text: context.read(path) }));
    const seen = new Set(handed.map((entry) => entry.path));
    return [...handed, ...onDisk.filter((entry) => !seen.has(entry.path))].filter(
        (entry) => entry.path !== BOOTSTRAP_DOCUMENT,
    );
};

const leftoverFinding = function leftoverFinding(path: string): Finding {
    return {
        actual: `${path} names ${BEHAVIOUR_TREE_PLACEHOLDER}, and no folder by that name exists`,
        expected: `${path} naming ${surfacePath("behaviour_tree")}, the folder the behavior tree was renamed to`,
        healed: false,
        line: 0,
        locus: BEHAVIOUR_TREE_PLACEHOLDER,
        path,
        remediation: {
            action: "rename",
            decide:
                "adoption renames the behavior folder from its shipped placeholder and replaces the placeholder wherever the package names it; this file still names the placeholder, so a path in it points at a folder that no longer exists. Replace the placeholder with the folder's new name",
            deterministic: false,
            from: BEHAVIOUR_TREE_PLACEHOLDER,
            target: path,
            to: null,
        },
        rule: "declaration/unrenamedPlaceholder",
        stack: [
            { check: "placeholderFolder", resolved: "absent" },
            { check: "named", resolved: path },
        ],
    };
};

const placeholderFindings = function placeholderFindings(context: RuleContext): Finding[] {
    if (existsSync(resolve(context.repoRoot, inSurfaceRoot(BEHAVIOUR_TREE_PLACEHOLDER)))) {
        return [];
    }
    return placeholderSources(context)
        .filter((entry) => entry.text.includes(BEHAVIOUR_TREE_PLACEHOLDER))
        .map((entry) => leftoverFinding(entry.path));
};

export const rule: RuleDeclaration = {
    check(context: RuleContext): RuleResult {
        const data = context.taxonomy;
        const { repoRoot } = context;

        const roots = [
            ...new Set<string>([
                ...Object.keys(data.containers),
                ...Object.keys(data.specialContainers),
                ...Object.keys(data.corpusRoots),
            ]),
        ];
        const upstream = UPSTREAM_ROOTS.map((root) => (root.endsWith("/") ? root.slice(0, -1) : root));
        const resolvedRoots = roots.filter((root) => isDirectory(repoRoot, root));
        const resolvedUpstream = upstream.filter((root) => isDirectory(repoRoot, root));

        const consumers = underRoots(context.paths, AXIS_GOVERNED).flatMap((path) =>
            axisConsumers(context.read(path), AXIS_REACHES).map((consumer) => ({ consumer, path })),
        );
        const axisInspected = consumers.map(
            ({ consumer, path }) =>
                `${path}:${consumer.name} asserts ${consumer.asserted}, reads ${readsOf(consumer, "+", "no axis")}`,
        );

        const findings = [
            ...rootFindings(repoRoot, roots, upstream),
            ...containerFindings(repoRoot, data),
            ...foreignFindings(repoRoot, roots, data),
            ...artifactFindings(repoRoot, data),
            ...inspectManifests(repoRoot, data.ignored),
            ...consumers
                .filter(({ consumer }) => silentOnItsOwnAxis(consumer))
                .map(({ consumer, path }) => axisFinding(path, consumer)),
            ...placeholderFindings(context),
        ];

        return {
            derivations: {
                axisInspected,
                declarationsWalked: resolvedRoots.length + resolvedUpstream.length,
                resolvedRoots,
                resolvedUpstream,
            },
            findings,
            healed: [],
        };
    },
    extensions: [],

    heals: false,
    invariant: "every declared root, container, bucket and corpus root resolves to a directory on disk",
    jurisdiction: "taxonomy",
    kinds: [
        "missingRoot",
        "missingContainer",
        "missingCorpusSubtree",
        "missingUpstreamRoot",
        "foreignGrammarClaimed",
        "unresolvedArtifactRoot",
        "unreadAssertedAxis",
        "declaredRuntimeDependency",
        "unreachedDependency",
        "unrenamedPlaceholder",
    ],
    readsTree:
        "the invariant is that a declared root resolves to a directory that exists, and a declared " +
        "directory holding no files contributes no paths at all — so a path set cannot tell a root that " +
        "is declared and empty from one that is declared and absent, which is the case this rule exists " +
        "to catch",
    stage: "meta",

    wholeScopeOnly: true,
};
```
