# tools/core/runners/blocking.runner.ts

> 153 lines of code and 27 definitions.

Tree: Coordination tree
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-runners-blocking-runner-ts
Source text: https://banes-lab.com/source/coordination/tools/core/runners/blocking.runner.ts.txt

Listed in [tools/core/runners](https://banes-lab.com/api/source/coordination/tools/core/runners.md), after [tools/core/runners/archive.runner.ts](https://banes-lab.com/source/coordination/tools/core/runners/archive.runner.ts.md) and before [tools/core/runners/board.runner.ts](https://banes-lab.com/source/coordination/tools/core/runners/board.runner.ts.md).

## Definitions

- `runDefer` (lexical_declaration, line 87, exported)
- `runRetract` (lexical_declaration, line 147, exported)
- `deferNothing` (lexical_declaration, line 37)
- `receiverKnown` (lexical_declaration, line 64)
- `deferRefusal` (lexical_declaration, line 75)
- `DeferRequest` (interface_declaration, line 29)
- `held` (lexical_declaration, line 38)
- `section` (lexical_declaration, line 39)
- `standing` (lexical_declaration, line 44)
- `marked` (lexical_declaration, line 50)
- `witnessed` (lexical_declaration, line 54)
- `venueNames` (lexical_declaration, line 65)
- `board` (lexical_declaration, line 69)
- `index` (lexical_declaration, line 70)
- `planned` (lexical_declaration, line 71)
- `refused` (lexical_declaration, line 88, exported)
- `deferral` (lexical_declaration, line 120, exported)
- `deferredClauseOf` (lexical_declaration, line 137)
- `trimmed` (lexical_declaration, line 138)
- `arrow` (lexical_declaration, line 143)
- `before` (lexical_declaration, line 160, exported)
- `bound` (lexical_declaration, line 161, exported)
- `lines` (lexical_declaration, line 166, exported)
- `listed` (lexical_declaration, line 167, exported)
- `at` (lexical_declaration, line 174, exported)
- `written` (lexical_declaration, line 175, exported)
- `witness` (lexical_declaration, line 177, exported)

## Uses

- [tools/core/analyzers/converge.analyzer.ts](https://banes-lab.com/source/coordination/tools/core/analyzers/converge.analyzer.ts.md)
- [tools/core/factories/venue.factory.ts](https://banes-lab.com/source/coordination/tools/core/factories/venue.factory.ts.md)
- [tools/core/resolvers/venue.resolver.ts](https://banes-lab.com/source/coordination/tools/core/resolvers/venue.resolver.ts.md)
- [tools/core/strings/venue.strings.ts](https://banes-lab.com/source/coordination/tools/core/strings/venue.strings.ts.md)

## Used by

- [tools/core/coordinators/venue.coordinator.ts](https://banes-lab.com/source/coordination/tools/core/coordinators/venue.coordinator.ts.md)

## Source

```typescript
import { AGENDA, BLOCKING_SUFFIX, LIST_MARKER, RECORD_ABSENT } from "../constants/blocking.constants.ts";
import {
    CLAUSE_IS_RECEIVER,
    DEFER_NOT_VENUE,
    RETRACT_NOT_VENUE,
    clauseDeferred,
    clauseRetracted,
    clauseTaken,
    deferVenueMissing,
    deferredSectionMissing,
    defersAlready,
    defersNothing,
    markerContended,
    receiverUnknown,
    retractClauseMissing,
    retractSectionMissing,
    retractVenueMissing,
} from "../strings/venue.strings.ts";
import { DEFERRAL_ARROW, DEFERRED_SECTION, clauseLines, sectionBound } from "../analyzers/converge.analyzer.ts";
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
import type { RaiseOutcome } from "../types/venue.types.ts";
import { dirname } from "node:path";
import { plannedInvariants } from "../readers/agenda.reader.ts";
import { receiverResolves } from "../resolvers/venue.resolver.ts";
import { surfacePath } from "../../../config/surface.config.ts";
import { surfaceText } from "../readers/venue.reader.ts";
import { venueRefusal } from "../factories/venue.factory.ts";

interface DeferRequest {
    readonly repoRoot: string;
    readonly target: string;
    readonly absolute: string;
    readonly clause: string;
    readonly receiver: string;
}

const deferNothing = function deferNothing(options: DeferRequest): RaiseOutcome {
    const held = readFileSync(options.absolute, "utf8");
    const section = sectionBound(held, DEFERRED_SECTION);
    if (section === null) {
        return venueRefusal(deferredSectionMissing(options.target, DEFERRED_SECTION));
    }

    const standing = clauseLines(held, DEFERRED_SECTION);
    if (standing.length > 0) {
        return venueRefusal(defersAlready(options.target, standing.length));
    }

    const lines = held.split("\n");
    const marked = [...lines.slice(0, section.to), `${LIST_MARKER}${RECORD_ABSENT}`, ...lines.slice(section.to)].join(
        "\n",
    );

    const witnessed = readFileSync(options.absolute, "utf8");
    if (witnessed !== held) {
        return venueRefusal(markerContended(options.target));
    }

    writeFileSync(options.absolute, marked, "utf8");

    return { code: 0, message: defersNothing(options.target), raised: null };
};

const receiverKnown = function receiverKnown(options: DeferRequest): boolean {
    const venueNames = readdirSync(dirname(options.absolute), { withFileTypes: true })
        .filter((entry) => entry.isFile() && entry.name.endsWith(BLOCKING_SUFFIX))
        .map((entry) => entry.name);

    const board = surfaceText(options.repoRoot, surfacePath("board"));
    const index = surfaceText(options.repoRoot, surfacePath("agent_index"));
    const planned = plannedInvariants(surfaceText(options.repoRoot, AGENDA));
    return receiverResolves(options.receiver, board, index, venueNames, planned);
};

const deferRefusal = function deferRefusal(options: DeferRequest): RaiseOutcome | null {
    if (!options.target.endsWith(BLOCKING_SUFFIX)) {
        return venueRefusal(DEFER_NOT_VENUE);
    }

    if (!existsSync(options.absolute)) {
        return venueRefusal(deferVenueMissing(options.target));
    }

    return null;
};

export const runDefer = function runDefer(options: DeferRequest): RaiseOutcome {
    const refused = deferRefusal(options);
    if (refused !== null) {
        return refused;
    }

    if (options.clause === RECORD_ABSENT) {
        return deferNothing(options);
    }

    if (options.receiver === options.clause) {
        return venueRefusal(CLAUSE_IS_RECEIVER);
    }

    if (!receiverKnown(options)) {
        return venueRefusal(receiverUnknown(options.receiver));
    }

    const before = readFileSync(options.absolute, "utf8");
    const bound = sectionBound(before, DEFERRED_SECTION);
    if (bound === null) {
        return venueRefusal(deferredSectionMissing(options.target, DEFERRED_SECTION));
    }

    if (clauseLines(before, DEFERRED_SECTION).some(({ clause }) => clause === options.clause)) {
        return venueRefusal(clauseTaken(options.target, options.clause));
    }

    const lines = before.split("\n");
    const listed = lines
        .slice(bound.from + 1, Math.min(bound.to, lines.length))
        .findLastIndex((line) => line.trim().startsWith(LIST_MARKER));
    const at = listed === -1 ? bound.to - 1 : bound.from + 1 + listed;
    const deferral = `- ${options.clause} ${DEFERRAL_ARROW} ${options.receiver}`;
    const written = [...lines.slice(0, at + 1), deferral, ...lines.slice(at + 1)].join("\n");

    const witness = readFileSync(options.absolute, "utf8");
    if (witness !== before) {
        return venueRefusal(markerContended(options.target));
    }

    writeFileSync(options.absolute, written, "utf8");

    return {
        code: 0,
        message: clauseDeferred(options.clause, options.receiver, options.target),
        raised: options.clause,
    };
};

const deferredClauseOf = function deferredClauseOf(line: string): string | null {
    const trimmed = line.trim();
    if (!trimmed.startsWith(LIST_MARKER)) {
        return null;
    }

    const arrow = trimmed.indexOf(DEFERRAL_ARROW);
    return (arrow === -1 ? trimmed.slice(2) : trimmed.slice(2, arrow)).trim();
};

export const runRetract = function runRetract(options: {
    readonly target: string;
    readonly absolute: string;
    readonly clause: string;
}): RaiseOutcome {
    if (!options.target.endsWith(BLOCKING_SUFFIX)) {
        return venueRefusal(RETRACT_NOT_VENUE);
    }

    if (!existsSync(options.absolute)) {
        return venueRefusal(retractVenueMissing(options.target));
    }

    const before = readFileSync(options.absolute, "utf8");
    const bound = sectionBound(before, DEFERRED_SECTION);
    if (bound === null) {
        return venueRefusal(retractSectionMissing(options.target, DEFERRED_SECTION));
    }

    const lines = before.split("\n");
    const listed = lines
        .slice(bound.from + 1, Math.min(bound.to, lines.length))
        .findLastIndex((line) => deferredClauseOf(line) === options.clause);
    if (listed === -1) {
        return venueRefusal(retractClauseMissing(options.target, options.clause));
    }

    const at = bound.from + 1 + listed;
    const written = [...lines.slice(0, at), ...lines.slice(at + 1)].join("\n");

    const witness = readFileSync(options.absolute, "utf8");
    if (witness !== before) {
        return venueRefusal(markerContended(options.target));
    }

    writeFileSync(options.absolute, written, "utf8");

    return { code: 0, message: clauseRetracted(options.clause, options.target), raised: options.clause };
};
```
