# tools/core/readers/agenda.reader.ts

> 101 lines of code and 31 definitions.

Tree: Coordination tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-readers-agenda-reader-ts
Source text: https://banes-lab.com/source/coordination/tools/core/readers/agenda.reader.ts.txt

Listed in [tools/core/readers](https://banes-lab.com/api/source/coordination/tools/core/readers.md), before [tools/core/readers/architecture.reader.ts](https://banes-lab.com/source/coordination/tools/core/readers/architecture.reader.ts.md).

## Definitions

- `agendaRows` (lexical_declaration, line 50, exported)
- `disorderedRows` (lexical_declaration, line 85, exported)
- `admissibleInvariants` (lexical_declaration, line 117, exported)
- `stateCellOf` (lexical_declaration, line 21)
- `agendaRowOf` (lexical_declaration, line 36)
- `agendaOrdinalOf` (lexical_declaration, line 103, exported)
- `agendaInvariants` (lexical_declaration, line 107, exported)
- `plannedInvariants` (lexical_declaration, line 111, exported)
- `nextUnraised` (lexical_declaration, line 123, exported)
- `AGENDA_MARK` (lexical_declaration, line 1)
- `ROW_LEAD` (lexical_declaration, line 3)
- `PLANNED_STATE` (lexical_declaration, line 5)
- `LETTER_SCALE` (lexical_declaration, line 7)
- `AgendaRow` (interface_declaration, line 9)
- `DisorderedRow` (interface_declaration, line 15)
- `cells` (lexical_declaration, line 22)
- `trimmed` (lexical_declaration, line 37)
- `opens` (lexical_declaration, line 38)
- `closes` (lexical_declaration, line 39)
- `invariant` (lexical_declaration, line 40)
- `cell` (lexical_declaration, line 45)
- `ordinal` (lexical_declaration, line 46)
- `row` (lexical_declaration, line 54, exported)
- `ordinalValue` (lexical_declaration, line 60, exported)
- `digits` (lexical_declaration, line 61, exported)
- `cursor` (lexical_declaration, line 62, exported)
- `suffix` (lexical_declaration, line 73, exported)
- `char` (lexical_declaration, line 75, exported)
- `out` (lexical_declaration, line 86, exported)
- `held` (lexical_declaration, line 87, exported)
- `value` (lexical_declaration, line 90, exported)

## Used by

- [tools/rules/blocking.rule.ts](https://banes-lab.com/source/coordination/tools/rules/blocking.rule.ts.md)

## Source

```typescript
const AGENDA_MARK = "`";

const ROW_LEAD = "|";

const PLANNED_STATE = "planned";

const LETTER_SCALE = 100;

interface AgendaRow {
    readonly invariant: string;
    readonly ordinal: string;
    readonly planned: boolean;
}

interface DisorderedRow {
    readonly invariant: string;
    readonly ordinal: string;
    readonly after: string;
}

const stateCellOf = function stateCellOf(row: string): string {
    const cells = row.split(ROW_LEAD);
    if (cells.length < 3) {
        return "";
    }

    const cell = (cells.at(-2) ?? "").trim();
    if (!cell.startsWith(AGENDA_MARK)) {
        return cell;
    }

    const closes = cell.indexOf(AGENDA_MARK, 1);
    return closes === -1 ? cell.slice(1) : cell.slice(1, closes);
};

const agendaRowOf = function agendaRowOf(line: string): AgendaRow | null {
    const trimmed = line.trim();
    const opens = trimmed.startsWith(ROW_LEAD) ? trimmed.indexOf(AGENDA_MARK) : -1;
    const closes = opens === -1 ? -1 : trimmed.indexOf(AGENDA_MARK, opens + 1);
    const invariant = closes === -1 ? "" : trimmed.slice(opens + 1, closes).trim();
    if (invariant.length === 0) {
        return null;
    }

    const cell = trimmed.indexOf(ROW_LEAD, 1);
    const ordinal = cell === -1 ? "" : trimmed.slice(1, cell).trim();
    return { invariant, ordinal, planned: stateCellOf(trimmed).startsWith(PLANNED_STATE) };
};

export const agendaRows = function agendaRows(agenda: string): AgendaRow[] {
    return agenda
        .split("\n")
        .flatMap((line) => {
            const row = agendaRowOf(line);
            return row === null ? [] : [row];
        })
        .filter((row, index, rows) => rows.findIndex((held) => held.invariant === row.invariant) === index);
};

export const ordinalValue = function ordinalValue(ordinal: string): number | null {
    let digits = "";
    let cursor = 0;

    while (cursor < ordinal.length && ordinal.charAt(cursor) >= "0" && ordinal.charAt(cursor) <= "9") {
        digits += ordinal.charAt(cursor);
        cursor += 1;
    }

    if (digits.length === 0) {
        return null;
    }

    let suffix = 0;
    for (; cursor < ordinal.length; cursor += 1) {
        const char = ordinal.charAt(cursor);
        if (char < "a" || char > "z") {
            return null;
        }
        suffix = suffix * LETTER_SCALE + ((char.codePointAt(0) ?? 0) - ("a".codePointAt(0) ?? 0) + 1);
    }

    return Number(digits) + suffix / (LETTER_SCALE * LETTER_SCALE);
};

export const disorderedRows = function disorderedRows(agenda: string): DisorderedRow[] {
    const out: DisorderedRow[] = [];
    let held: { value: number; invariant: string } | null = null;

    for (const row of agendaRows(agenda)) {
        const value = ordinalValue(row.ordinal);
        if (value !== null) {
            if (held !== null && value < held.value) {
                out.push({ after: held.invariant, invariant: row.invariant, ordinal: row.ordinal });
            } else {
                held = { invariant: row.invariant, value };
            }
        }
    }

    return out;
};

export const agendaOrdinalOf = function agendaOrdinalOf(agenda: string, invariant: string): string {
    return agendaRows(agenda).find((row) => row.invariant === invariant)?.ordinal ?? "";
};

export const agendaInvariants = function agendaInvariants(agenda: string): string[] {
    return agendaRows(agenda).map((row) => row.invariant);
};

export const plannedInvariants = function plannedInvariants(agenda: string): string[] {
    return agendaRows(agenda)
        .filter((row) => row.planned)
        .map((row) => row.invariant);
};

export const admissibleInvariants = function admissibleInvariants(agenda: string, venues: readonly string[]): string[] {
    return agendaRows(agenda)
        .filter((row) => row.planned && !venues.some((venue) => venue.includes(row.invariant)))
        .map((row) => row.invariant);
};

export const nextUnraised = function nextUnraised(agenda: string, venues: readonly string[]): string | null {
    return admissibleInvariants(agenda, venues)[0] ?? null;
};
```
