# tools/core/registries/claim.registry.ts

> 162 lines of code and 42 definitions.

Tree: Coordination tree
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-registries-claim-registry-ts
Source text: https://banes-lab.com/assets/sources/source.56aa88d49328c6cd8a9522751c66d98287bb51ea4299f2ec952d5a6eb481b39e.generated.txt

## Definitions

- `textOf` (lexical_declaration, line 54)
- `releaseStanding` (lexical_declaration, line 189, exported)
- `countOf` (lexical_declaration, line 58)
- `described` (lexical_declaration, line 136)
- `claimOf` (lexical_declaration, line 62)
- `witnessedGone` (lexical_declaration, line 114)
- `keyOf` (lexical_declaration, line 46)
- `codeOf` (lexical_declaration, line 103)
- `writeClaim` (lexical_declaration, line 146)
- `covers` (lexical_declaration, line 95)
- `writeSetsOverlap` (lexical_declaration, line 99, exported)
- `onThisHost` (lexical_declaration, line 110)
- `claimStanding` (lexical_declaration, line 151, exported)
- `liveWindow` (lexical_declaration, line 50)
- `claimRoot` (lexical_declaration, line 42)
- `heldClaims` (lexical_declaration, line 85, exported)
- `stillRunning` (lexical_declaration, line 131)
- `CLAIMS` (lexical_declaration, line 11)
- `TAIL` (lexical_declaration, line 13)
- `UNKNOWN` (lexical_declaration, line 15)
- `WHOLE` (lexical_declaration, line 17)
- `NO_PROCESS` (lexical_declaration, line 19)
- `LIVENESS_PROBE` (lexical_declaration, line 21)
- `MILLISECONDS` (lexical_declaration, line 23)
- `RunClaim` (interface_declaration, line 25, exported)
- `ClaimStanding` (interface_declaration, line 34, exported)
- `read` (lexical_declaration, line 63)
- `{ value }` (lexical_declaration, line 68)
- `ageOf` (lexical_declaration, line 127)
- `age` (lexical_declaration, line 137)
- `since` (lexical_declaration, line 138)
- `precedes` (lexical_declaration, line 142)
- `mine` (lexical_declaration, line 158, exported)
- `others` (lexical_declaration, line 161, exported)
- `window` (lexical_declaration, line 162, exported)
- `running` (lexical_declaration, line 163, exported)
- `dead` (lexical_declaration, line 164, exported)
- `live` (lexical_declaration, line 170, exported)
- `incomplete` (lexical_declaration, line 171, exported)
- `yields` (lexical_declaration, line 172, exported)
- `lines` (lexical_declaration, line 173, exported)
- `root` (lexical_declaration, line 194, exported)

## Uses

- [config/surface.config.ts](https://banes-lab.com/source/coordination/config/surface.config.ts.md)
- [tools/core/formatters/board.formatter.ts](https://banes-lab.com/source/coordination/tools/core/formatters/board.formatter.ts.md)
- [tools/core/formatters/text.formatter.ts](https://banes-lab.com/source/coordination/tools/core/formatters/text.formatter.ts.md)

## Used by

- [tools/core/entrypoints/pipeline.entrypoint.ts](https://banes-lab.com/source/coordination/tools/core/entrypoints/pipeline.entrypoint.ts.md)

## Source

```typescript
import { RELEASE_UNIDENTIFIED, runsAbandoned, runsInFlight } from "../strings/claim.strings.ts";
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
import { fieldOf, tryParse } from "../readers/json.reader.ts";
import { isResolved, slotCount } from "../../../config/surface.config.ts";

import { GENERATED_DIR } from "../constants/path.constants.ts";
import { hostname } from "node:os";
import { join } from "node:path";
import { safeKey } from "../formatters/text.formatter.ts";

const CLAIMS = "run.claims";

const TAIL = ".claim.generated.json";

const UNKNOWN = "unknown";

const WHOLE = "whole";

const NO_PROCESS = "ESRCH";

const LIVENESS_PROBE = 0;

const MILLISECONDS = 1000;

export interface RunClaim {
    readonly id: string;
    readonly scope: string;
    readonly at: number;
    readonly agent: string;
    readonly pid: number;
    readonly host: string;
}

export interface ClaimStanding {
    readonly decision: "proceed" | "yield";
    readonly covering: readonly string[];
    readonly overlapping: readonly string[];
    readonly incomplete: readonly string[];
    readonly message: string | null;
}

const claimRoot = function claimRoot(repoRoot: string): string {
    return join(repoRoot, GENERATED_DIR, CLAIMS);
};

const keyOf = function keyOf(agent: string, at: number): string {
    return `${safeKey(agent)}-${String(at)}`;
};

const liveWindow = function liveWindow(): number | null {
    return isResolved("convention", "run_live_window_ms") ? slotCount("convention", "run_live_window_ms") : null;
};

const textOf = function textOf(value: unknown, fallback: string): string {
    return typeof value === "string" ? value : fallback;
};

const countOf = function countOf(value: unknown): number {
    return typeof value === "number" ? value : 0;
};

const claimOf = function claimOf(root: string, entry: string): RunClaim[] {
    const read = tryParse(readFileSync(join(root, entry), "utf8"));
    if (read === null) {
        return [{ agent: UNKNOWN, at: 0, host: UNKNOWN, id: entry, pid: 0, scope: UNKNOWN }];
    }

    const { value } = read;
    if (typeof value !== "object" || value === null) {
        return [];
    }

    return [
        {
            agent: textOf(fieldOf(value, "agent"), UNKNOWN),
            at: countOf(fieldOf(value, "at")),
            host: textOf(fieldOf(value, "host"), UNKNOWN),
            id: entry.slice(0, entry.length - TAIL.length),
            pid: countOf(fieldOf(value, "pid")),
            scope: textOf(fieldOf(value, "scope"), UNKNOWN),
        },
    ];
};

export const heldClaims = function heldClaims(repoRoot: string): RunClaim[] {
    const root = claimRoot(repoRoot);
    if (!existsSync(root)) {
        return [];
    }
    return readdirSync(root)
        .filter((entry) => entry.endsWith(TAIL))
        .flatMap((entry) => claimOf(root, entry));
};

const covers = function covers(held: string, mine: string): boolean {
    return held === WHOLE || held === mine;
};

export const writeSetsOverlap = function writeSetsOverlap(held: string, mine: string): boolean {
    return held === WHOLE || mine === WHOLE || held === mine;
};

const codeOf = function codeOf(error: unknown): string {
    if (typeof error !== "object" || error === null) {
        return "";
    }
    return textOf(fieldOf(error, "code"), "");
};

const onThisHost = function onThisHost(claim: RunClaim): boolean {
    return claim.pid > 0 && claim.host !== UNKNOWN && claim.host === hostname();
};

const witnessedGone = function witnessedGone(claim: RunClaim): boolean {
    if (!onThisHost(claim)) {
        return false;
    }

    try {
        process.kill(claim.pid, LIVENESS_PROBE);
        return false;
    } catch (error) {
        return codeOf(error) === NO_PROCESS;
    }
};

const ageOf = function ageOf(claim: RunClaim, at: number): number | null {
    return claim.at === 0 ? null : at - claim.at;
};

const stillRunning = function stillRunning(claim: RunClaim, at: number, window: number | null): boolean {
    const age = ageOf(claim, at);
    return !witnessedGone(claim) && window !== null && age !== null && Math.abs(age) < window;
};

const described = function described(claim: RunClaim, at: number): string {
    const age = ageOf(claim, at);
    const since = age === null ? "" : ` ${String(Math.round(age / MILLISECONDS))}s ago`;
    return `${claim.scope} by ${claim.agent}${since}`;
};

const precedes = function precedes(claim: RunClaim, at: number, agent: string): boolean {
    return claim.at === at ? claim.agent.localeCompare(agent, "en") < 0 : claim.at < at;
};

const writeClaim = function writeClaim(root: string, mine: string, entry: Omit<RunClaim, "id">): void {
    mkdirSync(root, { recursive: true });
    writeFileSync(join(root, `${mine}${TAIL}`), `${JSON.stringify(entry, null, 4)}\n`, "utf8");
};

export const claimStanding = function claimStanding(
    repoRoot: string,
    scope: string,
    at: number,
    agent: string,
): ClaimStanding {
    const root = claimRoot(repoRoot);
    const mine = keyOf(agent, at);
    writeClaim(root, mine, { agent, at, host: hostname(), pid: process.pid, scope });

    const others = heldClaims(repoRoot).filter((claim) => claim.id !== mine);
    const window = liveWindow();
    const running = others.filter((claim) => stillRunning(claim, at, window));
    const dead = others.filter((claim) => !running.includes(claim));

    for (const claim of dead) {
        rmSync(join(root, `${claim.id}${TAIL}`), { force: true });
    }

    const live = running.map((claim) => described(claim, at));
    const incomplete = dead.map((claim) => described(claim, at));
    const yields = running.some((claim) => precedes(claim, at, agent));
    const lines = [
        ...(live.length > 0 ? [runsInFlight(yields, live)] : []),
        ...(incomplete.length > 0 ? [runsAbandoned(incomplete)] : []),
    ];

    return {
        covering: running.filter((claim) => covers(claim.scope, scope)).map((claim) => described(claim, at)),
        decision: yields ? "yield" : "proceed",
        incomplete,
        message: lines.length === 0 ? null : lines.join(". "),
        overlapping: running
            .filter((claim) => writeSetsOverlap(claim.scope, scope))
            .map((claim) => described(claim, at)),
    };
};

export const releaseStanding = function releaseStanding(repoRoot: string, agent: string, at: number): string | null {
    if (agent.length === 0 || at <= 0) {
        return RELEASE_UNIDENTIFIED;
    }

    const root = claimRoot(repoRoot);
    if (existsSync(root)) {
        rmSync(join(root, `${keyOf(agent, at)}${TAIL}`), { force: true });
    }
    return null;
};
```
