tools/core/runners/mark.runner.ts
tools/core/runners/mark.runner.ts is a file in Coordination Surface. 163 lines of code and 42 definitions.
import { AT_FIELD, READ_FIELD, TO_FIELD } from "../formatters/board.formatter.ts";
import { AWAITING_MARKER, BLOCKING_SUFFIX, RECORD_ABSENT, UNREAD_MARKER } from "../constants/blocking.constants.ts";
import { alreadyMarked, itemNotFound, marked, notAddressed } from "../strings/mark.strings.ts";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { letters, rosterLine } from "./converge.runner.ts";
import {
rosterContended,
rosterLinesMissing,
rosterNotVenue,
rosterRegression,
rosterResolved,
rosterUnchanged,
rosterVenueElsewhere,
} from "../strings/venue.strings.ts";
import { surfacePath, surfacePrefix } from "../../../config/surface.config.ts";
import type { RaiseOutcome } from "../types/venue.types.ts";
import { activeSeats } from "../analyzers/board.analyzer.ts";
import { itemSpans } from "../resolvers/sweep.resolver.ts";
import { resolve } from "node:path";
import { surfaceText } from "../readers/venue.reader.ts";
import { venueRefusal } from "../factories/venue.factory.ts";
interface MarkRequest {
readonly target: string;
readonly absolute: string;
readonly item: string;
readonly agent: string;
}
interface MarkOutcome {
readonly code: number;
readonly message: string;
}
export const markedReaders = function markedReaders(marker: string): string[] {
const at = marker.indexOf(READ_FIELD);
if (at === -1) {
return [];
}
const rest = marker.slice(at + READ_FIELD.length);
let value = "";
for (const character of rest) {
if (character === " ") {
break;
}
value += character;
}
return value.length === 0 ? [] : value.split(",");
};
const withReader = function withReader(marker: string, agent: string): string {
const readers = markedReaders(marker);
if (readers.includes(agent)) {
return marker;
}
const joined = [...readers, agent].join(",");
const at = marker.indexOf(READ_FIELD);
if (at === -1) {
return `${marker} ${READ_FIELD}${joined}`;
}
const tail = marker.slice(at + READ_FIELD.length);
let width = 0;
while (width < tail.length && tail.charAt(width) !== " ") {
width += 1;
}
return `${marker.slice(0, at)}${READ_FIELD}${joined}${tail.slice(width)}`;
};
export const runMark = function runMark(request: MarkRequest): MarkOutcome {
const source = readFileSync(request.absolute, "utf8");
const span = itemSpans(source).find((entry) => entry.key === request.item);
if (span === undefined) {
return { code: 2, message: itemNotFound(request.item, request.target) };
}
if (span.to.length > 0 && !span.to.includes(request.agent)) {
return { code: 2, message: notAddressed(request.item, request.agent) };
}
const lines = source.split("\n");
const marker = lines[span.from] ?? "";
if (markedReaders(marker).includes(request.agent)) {
return { code: 0, message: alreadyMarked(request.agent, request.item) };
}
lines[span.from] = withReader(marker, request.agent);
writeFileSync(request.absolute, lines.join("\n"), "utf8");
return { code: 0, message: marked(request.agent, request.item) };
};
export const healRequested = function healRequested(argv: readonly string[], flag: string): boolean {
return !argv.includes(flag);
};
export const authorOf = function authorOf(item: string): string {
const cut = item.indexOf("-");
return cut <= 0 ? item : item.slice(0, cut);
};
export const unmarkedReaders = function unmarkedReaders(
marker: string,
addressed: readonly string[],
active: readonly string[],
author = "",
): string[] {
const readers = markedReaders(marker);
const named = addressed.length === 0 ? active : addressed;
return named.filter((letter) => letter !== author && active.includes(letter) && !readers.includes(letter));
};
export const carriesLedger = function carriesLedger(marker: string): boolean {
return marker.includes(READ_FIELD) || marker.includes(TO_FIELD) || marker.includes(AT_FIELD);
};
export const rosterFor = function rosterFor(
repoRoot: string,
markedLetters: readonly string[],
): { unread: string[]; read: string[] } {
const board = surfaceText(repoRoot, surfacePath("board"));
const index = surfaceText(repoRoot, surfacePath("agent_index"));
const seats = activeSeats(board, index);
const read = seats.filter((letter) => markedLetters.includes(letter));
return { read, unread: seats.filter((letter) => !markedLetters.includes(letter)) };
};
const rosterRewrite = function rosterRewrite(
repoRoot: string,
name: string,
before: string,
): RaiseOutcome | { composed: string; roster: { unread: string[]; read: string[] } } {
const lines = before.split("\n");
const unreadAt = lines.findIndex((line) => line.startsWith(UNREAD_MARKER));
const readAt = lines.findIndex((line) => line.startsWith(AWAITING_MARKER));
if (unreadAt === -1 || readAt === -1) {
return venueRefusal(rosterLinesMissing(name));
}
const roster = rosterFor(repoRoot, letters(lines[readAt] ?? "", AWAITING_MARKER));
const seated = new Set([...roster.unread, ...roster.read]);
const standing = letters(lines[unreadAt] ?? "", UNREAD_MARKER).filter((letter) => seated.has(letter));
const regressing = roster.unread.filter((letter) => !standing.includes(letter));
if (standing.length > 0 && regressing.length > 0) {
return venueRefusal(rosterRegression(name, regressing));
}
const written = [...lines];
written[unreadAt] = rosterLine(UNREAD_MARKER, roster.unread);
written[readAt] = rosterLine(AWAITING_MARKER, roster.read);
return { composed: written.join("\n"), roster };
};
export const runRoster = function runRoster(options: {
readonly repoRoot: string;
readonly name: string;
}): RaiseOutcome {
if (!options.name.endsWith(BLOCKING_SUFFIX)) {
return venueRefusal(rosterNotVenue(options.name));
}
const absolute = resolve(options.repoRoot, surfacePrefix(), options.name);
if (!existsSync(absolute)) {
return venueRefusal(rosterVenueElsewhere(options.name));
}
const before = readFileSync(absolute, "utf8");
const rewrite = rosterRewrite(options.repoRoot, options.name, before);
if (!("composed" in rewrite)) {
return rewrite;
}
const { composed, roster } = rewrite;
if (composed === before) {
return { code: 0, message: rosterUnchanged(options.name), raised: options.name };
}
const witness = readFileSync(absolute, "utf8");
if (witness !== before) {
return venueRefusal(rosterContended(options.name));
}
writeFileSync(absolute, composed, "utf8");
const unread = roster.unread.join(", ") || RECORD_ABSENT;
const read = roster.read.join(", ") || RECORD_ABSENT;
return { code: 0, message: rosterResolved(options.name, unread, read), raised: options.name };
};