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

> 81 lines of code and 20 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-runners-task-runner-ts
Source text: https://banes-lab.com/assets/sources/source.c4073fdfe1370e6081785801b1116de098a9fd686d49287148aa3020d08d1668.generated.txt

## Definitions

- `isDeclared` (lexical_declaration, line 37)
- `runTask` (lexical_declaration, line 62, exported)
- `TASK_MARKER` (lexical_declaration, line 16)
- `PLACEHOLDER` (lexical_declaration, line 18)
- `OWNER_MARKER` (lexical_declaration, line 20)
- `FIELD_DIVIDER` (lexical_declaration, line 22)
- `TaskRequest` (interface_declaration, line 24, exported)
- `TaskOutcome` (interface_declaration, line 32, exported)
- `lastTaskLine` (lexical_declaration, line 50)
- `index` (lexical_declaration, line 53)
- `absolute` (lexical_declaration, line 63, exported)
- `source` (lexical_declaration, line 73, exported)
- `lines` (lexical_declaration, line 79, exported)
- `at` (lexical_declaration, line 80, exported)
- `template` (lexical_declaration, line 85, exported)
- `markers` (lexical_declaration, line 86, exported)
- `fields` (lexical_declaration, line 91, exported)
- `row` (lexical_declaration, line 95, exported)
- `written` (lexical_declaration, line 96, exported)
- `outcome` (lexical_declaration, line 98, exported)

## Uses

- [tools/core/strings/task.strings.ts](https://banes-lab.com/source/coordination/tools/core/strings/task.strings.ts.md)

## Source

```typescript
import {
    ROW_INCOMPLETE,
    contractMissing,
    idTaken,
    noTaskRows,
    planMissing,
    rowAppended,
} from "../strings/task.strings.ts";
import { existsSync, readFileSync } from "node:fs";

import { PLANNING_TEMPLATE } from "../constants/checklist.constants.ts";
import { readRowMarkers } from "../readers/template.reader.ts";
import { resolve } from "node:path";
import { writeRepair } from "../writers/repair.writer.ts";

const TASK_MARKER = "- [ ] ";

const PLACEHOLDER = "<unwritten>";

const OWNER_MARKER = "*owner:*";

const FIELD_DIVIDER = " · ";

export interface TaskRequest {
    readonly repoRoot: string;
    readonly target: string;
    readonly id: string;
    readonly statement: string;
    readonly owner: string;
}

export interface TaskOutcome {
    readonly code: number;
    readonly message: string;
}

const isDeclared = function isDeclared(source: string, id: string): boolean {
    for (const line of source.split("\n")) {
        if (!line.trimStart().startsWith(TASK_MARKER)) {
            continue;
        }
        if (line.trimStart().slice(TASK_MARKER.length).startsWith(`${id} `)) {
            return true;
        }
    }

    return false;
};

const lastTaskLine = function lastTaskLine(lines: readonly string[]): number {
    let at = -1;

    for (let index = 0; index < lines.length; index += 1) {
        if ((lines[index] ?? "").trimStart().startsWith(TASK_MARKER)) {
            at = index;
        }
    }

    return at;
};

export const runTask = function runTask(request: TaskRequest): TaskOutcome {
    const absolute = resolve(request.repoRoot, request.target);

    if (!existsSync(absolute)) {
        return { code: 2, message: planMissing(request.target) };
    }

    if (request.id.trim().length === 0 || request.statement.trim().length === 0) {
        return { code: 2, message: ROW_INCOMPLETE };
    }

    const source = readFileSync(absolute, "utf8");

    if (isDeclared(source, request.id.trim())) {
        return { code: 2, message: idTaken(request.id, request.target) };
    }

    const lines = source.split("\n");
    const at = lastTaskLine(lines);
    if (at === -1) {
        return { code: 2, message: noTaskRows(request.target) };
    }

    const template = resolve(request.repoRoot, PLANNING_TEMPLATE);
    const markers = existsSync(template) ? readRowMarkers(readFileSync(template, "utf8")) : [];
    if (markers.length === 0) {
        return { code: 2, message: contractMissing(PLANNING_TEMPLATE) };
    }

    const fields = markers
        .map((marker) => `${marker} ${marker === OWNER_MARKER ? request.owner : PLACEHOLDER}`)
        .join(FIELD_DIVIDER);

    const row = `${TASK_MARKER}${request.id.trim()} ${request.statement.trim()}. ${fields}`;
    const written = [...lines.slice(0, at + 1), row, ...lines.slice(at + 1)];

    const outcome = writeRepair({ declared: "whole", repoRoot: request.repoRoot }, request.target, written.join("\n"));
    if (!outcome.written) {
        return { code: 2, message: outcome.refusal ?? "" };
    }

    return { code: 0, message: rowAppended(request.id, request.target) };
};
```
