# tools/rules/record.rule.ts

> 86 lines of code and 16 definitions.

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

## Definitions

- `typedFindings` (lexical_declaration, line 32)
- `citationFindings` (lexical_declaration, line 13)
- `documentFindings` (lexical_declaration, line 39)
- `CITATION_KEYS` (lexical_declaration, line 7)
- `RecordDocument` (type_alias_declaration, line 9)
- `IntelRecord` (type_alias_declaration, line 11)
- `value` (lexical_declaration, line 15)
- `cited` (lexical_declaration, line 16)
- `repeated` (lexical_declaration, line 45)
- `duplicate` (lexical_declaration, line 46)
- `rule` (lexical_declaration, line 62, exported)
- `check` (method_definition, line 63, exported)
- `paths` (lexical_declaration, line 64, exported)
- `parsed` (lexical_declaration, line 65, exported)
- `known` (lexical_declaration, line 66, exported)
- `findings` (lexical_declaration, line 67, exported)

## Uses

- [tools/core/validators/record.validator.ts](https://banes-lab.com/source/coordination/tools/core/validators/record.validator.ts.md)

## Source

```typescript
import type { RuleContext, RuleDeclaration, RuleResult } from "../core/types/rule.types.ts";
import { checkSchema, checkTension, recordFinding as finding } from "../core/validators/record.validator.ts";
import { citedIds, readRecordDocument } from "../core/readers/record.reader.ts";
import type { Finding } from "../core/types/segment.types.ts";
import { INTEL_ROOT } from "../core/constants/template.constants.ts";

const CITATION_KEYS = ["conflicts", "with", "compatible", "incompatible", "see"] as const;

type RecordDocument = ReturnType<typeof readRecordDocument>;

type IntelRecord = RecordDocument["records"][number];

const citationFindings = function citationFindings(path: string, record: IntelRecord, known: ReadonlySet<string>): Finding[] {
    return CITATION_KEYS.flatMap((key) => {
        const value = record.keys[key];
        const cited = value === undefined ? [] : citedIds(value);
        return cited
            .filter((id) => !known.has(id))
            .map((id) =>
                finding(
                    "unresolvedCitation",
                    path,
                    record.line,
                    `${record.id} ${key}`,
                    `${id} does not resolve to a record`,
                    "correct the id or drop the citation — a citation that resolves to nothing errors nowhere and disconnects the graph silently",
                ),
            );
    });
};

const typedFindings = function typedFindings(path: string, record: IntelRecord, document: RecordDocument): Finding[] {
    if (document.shape !== "typed") {
        return [];
    }
    return [...checkSchema(path, record), ...(record.keys["type"] === "tension" ? checkTension(path, record) : [])];
};

const documentFindings = function documentFindings(
    path: string,
    document: RecordDocument,
    known: ReadonlySet<string>,
): Finding[] {
    return document.records.flatMap((record, index) => {
        const repeated = document.records.slice(0, index).some((earlier) => earlier.id === record.id);
        const duplicate = repeated
            ? [
                  finding(
                      "duplicateId",
                      path,
                      record.line,
                      record.id,
                      `${record.id} is declared more than once`,
                      "record ids are stable and unique; renumber the later one rather than reusing an id another record already answers to",
                  ),
              ]
            : [];
        return [...duplicate, ...citationFindings(path, record, known), ...typedFindings(path, record, document)];
    });
};

export const rule: RuleDeclaration = {
    check(context: RuleContext): RuleResult {
        const paths = context.paths.filter((path) => path.startsWith(INTEL_ROOT));
        const parsed = paths.map((path) => ({ document: readRecordDocument(context.read(path)), path }));
        const known = new Set(parsed.flatMap(({ document }) => document.records.map((record) => record.id)));
        const findings = parsed.flatMap(({ document, path }) => documentFindings(path, document, known));

        return {
            derivations: {
                reached: paths,
                recordsWalked: [...known].toSorted((left, right) => left.localeCompare(right, "en")),
                skippedAsOutsideRecordRoot: context.paths.filter((path) => !path.startsWith(INTEL_ROOT)),
            },
            findings,
            healed: [],
        };
    },
    extensions: [".md"],
    heals: false,
    invariant: "every typed intel record carries its required keys and every cited record id resolves",
    jurisdiction: "taxonomy",
    kinds: [
        "disputedNotSurfaced",
        "duplicateId",
        "kindContradiction",
        "missingKey",
        "missingTensionKey",
        "unknownDerivation",
        "unknownKind",
        "unknownTier",
    ],

    stage: "content",
};
```
