# core/converters/lesson.converter.ts

> 99 lines of code and 27 definitions.

Tree: Bane's Lab Content
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/content#file-content-core-converters-lesson-converter-ts
Source text: https://banes-lab.com/source/content/core/converters/lesson.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/content/core/converters.md), after [core/converters/inventory.converter.ts](https://banes-lab.com/source/content/core/converters/inventory.converter.ts.md) and before [core/converters/principle.converter.ts](https://banes-lab.com/source/content/core/converters/principle.converter.ts.md).

## Definitions

- `textOrNull` (lexical_declaration, line 36)
- `clusterSeedOf` (lexical_declaration, line 62)
- `seedOf` (lexical_declaration, line 73)
- `formationOf` (lexical_declaration, line 50)
- `kernelSeedOf` (lexical_declaration, line 57)
- `kernelOf` (lexical_declaration, line 11)
- `validationOf` (lexical_declaration, line 15)
- `causesOf` (lexical_declaration, line 24)
- `governingOf` (lexical_declaration, line 32)
- `violationTextOf` (lexical_declaration, line 43)
- `algoSeeds` (lexical_declaration, line 110, exported)
- `SOURCE` (lexical_declaration, line 6)
- `LABEL_JOINER` (lexical_declaration, line 7)
- `SourcedField` (type_alias_declaration, line 9)
- `entry` (lexical_declaration, line 16)
- `record` (lexical_declaration, line 20)
- `refs` (lexical_declaration, line 25)
- `names` (lexical_declaration, line 28)
- `ARCH_PREFIX` (lexical_declaration, line 40)
- `LEX_PREFIX` (lexical_declaration, line 41)
- `before` (lexical_declaration, line 58)
- `FIELD_ABSENT` (lexical_declaration, line 88)
- `missingFieldsOf` (lexical_declaration, line 95, exported)
- `reportOf` (lexical_declaration, line 99, exported)
- `incomplete` (lexical_declaration, line 100, exported)
- `missing` (lexical_declaration, line 102, exported)
- `contracts` (lexical_declaration, line 111, exported)

## Contained in

- [core/converters](https://banes-lab.com/anatomy/content/folder-content-core-converters.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/content/folder-content-core-converters.md)

## Source

```typescript
import { ARCH_FACE, FACE_SEPARATOR, LEX_FACE } from "@govlab/constants";
import type { Contract, Principle } from "@govlab/context";
import type { Faces, IncompleteSeed, LessonSeed, SeedField, SeedReport } from "#types/lesson.types";
import { SEED_FIELDS, SEED_LEVELS, VERIFY_STAGE } from "#configuration/constants/seed.constants";

const SOURCE = "algorithms";
const LABEL_JOINER = ", ";

type SourcedField = "failureMode" | "problem" | "validation";

const kernelOf = function kernelOf(contracts: readonly Contract[], domain: string): Contract | null {
    return contracts.find((contract) => contract.domain === domain && contract.derivationMap !== undefined) ?? null;
};

const validationOf = function validationOf(faces: Faces, kernel: Contract | null): string | null {
    const entry = kernel?.derivationMap?.find((mapped) => mapped.stage === VERIFY_STAGE);
    if (entry === undefined) {
        return null;
    }
    const record = faces.algo.get(entry.record);
    return record === null || record.invariant.length === 0 ? null : record.invariant;
};

const causesOf = function causesOf(faces: Faces, meta: Contract, kernel: Contract | null): string[] {
    const refs = [meta, ...(kernel ? [kernel] : []), ...meta.composes.flatMap((id) => faces.algo.get(id) ?? [])]
        .map((contract) => contract.principleRef)
        .filter((ref): ref is string => typeof ref === "string");
    const names = new Set(refs.flatMap((ref) => faces.arch.get(ref)?.name ?? []));
    return [...names].sort((a, b) => a.localeCompare(b));
};

const governingOf = function governingOf(faces: Faces, meta: Contract): Principle | null {
    return typeof meta.principleRef === "string" ? faces.arch.get(meta.principleRef) : null;
};

const textOrNull = function textOrNull(value: string | undefined): string | null {
    return value === undefined || value.trim().length === 0 ? null : value;
};

const ARCH_PREFIX = ARCH_FACE + FACE_SEPARATOR;
const LEX_PREFIX = LEX_FACE + FACE_SEPARATOR;

const violationTextOf = function violationTextOf(faces: Faces, ref: string): string | undefined {
    if (ref.startsWith(ARCH_PREFIX)) {
        return faces.arch.get(ref.slice(ARCH_PREFIX.length))?.formed_by;
    }
    return ref.startsWith(LEX_PREFIX) ? faces.lex.get(ref.slice(LEX_PREFIX.length))?.definition : undefined;
};

const formationOf = function formationOf(faces: Faces, governing: Principle | null): string | undefined {
    if (governing === null || governing.formed_by !== undefined) {
        return governing?.formed_by;
    }
    return (governing.violated_by ?? []).map((ref) => violationTextOf(faces, ref)).find((text) => text !== undefined);
};

const kernelSeedOf = function kernelSeedOf(faces: Faces, kernel: Contract): Pick<LessonSeed, SourcedField> {
    const before = kernel.exemplar?.before ?? null;
    return { failureMode: before, problem: before, validation: validationOf(faces, kernel) };
};

const clusterSeedOf = function clusterSeedOf(
    faces: Faces,
    governing: Principle | null,
): Pick<LessonSeed, SourcedField> {
    return {
        failureMode: textOrNull(governing?.exemplar?.before),
        problem: textOrNull(formationOf(faces, governing)),
        validation: textOrNull(governing?.detected_by.join(LABEL_JOINER)),
    };
};

const seedOf = function seedOf(faces: Faces, meta: Contract, kernel: Contract | null): LessonSeed {
    return {
        application: meta.flow,
        boundary: null,
        cause: causesOf(faces, meta, kernel),
        decision: meta.intent,
        domain: meta.domain,
        id: meta.id,
        level: SEED_LEVELS[SOURCE],
        principle: meta.invariant,
        source: SOURCE,
        ...(kernel ? kernelSeedOf(faces, kernel) : clusterSeedOf(faces, governingOf(faces, meta))),
    };
};

const FIELD_ABSENT: Readonly<Record<SeedField, (seed: LessonSeed) => boolean>> = {
    cause: (seed) => seed.cause.length === 0,
    principle: (seed) => seed.principle.length === 0,
    problem: (seed) => seed.problem === null,
    validation: (seed) => seed.validation === null,
};

export const missingFieldsOf = function missingFieldsOf(seed: LessonSeed): string[] {
    return SEED_FIELDS[seed.source].filter((field) => FIELD_ABSENT[field](seed));
};

export const reportOf = function reportOf(seeds: readonly LessonSeed[]): SeedReport {
    const incomplete: IncompleteSeed[] = [];
    for (const seed of seeds) {
        const missing = missingFieldsOf(seed);
        if (missing.length > 0) {
            incomplete.push({ id: seed.id, missing });
        }
    }
    return { incomplete, seeds };
};

export const algoSeeds = function algoSeeds(faces: Faces): LessonSeed[] {
    const contracts = faces.algo.all();
    return faces.algo
        .query({ meta: true })
        .map((meta) => seedOf(faces, meta, kernelOf(contracts, meta.domain)))
        .sort((a, b) => a.id.localeCompare(b.id));
};
```
