# tools/core/reporters/rule.reporter.ts

> 152 lines of code and 28 definitions.

Tree: Coordination tree
Language: typescript
Layer: operations
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-reporters-rule-reporter-ts
Source text: https://banes-lab.com/assets/sources/source.da367d3fc89f778d831638afe899c16ad4385f887d482f4cb8c0fb42a7eb13d4.generated.txt

## Definitions

- `writeRuleReport` (lexical_declaration, line 66, exported)
- `write` (lexical_declaration, line 55)
- `ruleReportName` (lexical_declaration, line 62, exported)
- `supersedingRun` (lexical_declaration, line 93, exported)
- `writePipelineReport` (lexical_declaration, line 174, exported)
- `channelReportName` (lexical_declaration, line 76, exported)
- `channelScopeOf` (lexical_declaration, line 80, exported)
- `joinLiveRun` (lexical_declaration, line 145, exported)
- `RuleReport` (interface_declaration, line 10, exported)
- `StageSummary` (interface_declaration, line 24, exported)
- `PipelineReport` (interface_declaration, line 33, exported)
- `AGGREGATE_REPORT` (lexical_declaration, line 70, exported)
- `CHANNEL_LEAD` (lexical_declaration, line 72)
- `CHANNEL_TAIL` (lexical_declaration, line 74)
- `encoded` (lexical_declaration, line 85, exported)
- `JOIN_POLL_MS` (lexical_declaration, line 118)
- `JOIN_LIMIT_MS` (lexical_declaration, line 120)
- `publishedAfter` (lexical_declaration, line 122)
- `target` (lexical_declaration, line 123, exported)
- `parsed` (lexical_declaration, line 128, exported)
- `record` (lexical_declaration, line 139)
- `stamp` (lexical_declaration, line 140, exported)
- `deadline` (lexical_declaration, line 149, exported)
- `published` (lexical_declaration, line 152, exported)
- `verdict` (lexical_declaration, line 155, exported)
- `agent` (lexical_declaration, line 156, exported)
- `scope` (lexical_declaration, line 157, exported)
- `findings` (lexical_declaration, line 158, exported)

## Uses

- [tools/core/strings/pipeline.strings.ts](https://banes-lab.com/source/coordination/tools/core/strings/pipeline.strings.ts.md)
- [tools/core/transformers/scope.transformer.ts](https://banes-lab.com/source/coordination/tools/core/transformers/scope.transformer.ts.md)

## Used by

- [tools/core/orchestrators/pipeline.orchestrator.ts](https://banes-lab.com/source/coordination/tools/core/orchestrators/pipeline.orchestrator.ts.md)
- [tools/core/steps/gate.step.ts](https://banes-lab.com/source/coordination/tools/core/steps/gate.step.ts.md)
- [tools/core/steps/quality.step.ts](https://banes-lab.com/source/coordination/tools/core/steps/quality.step.ts.md)
- [tools/core/steps/snapshot.step.ts](https://banes-lab.com/source/coordination/tools/core/steps/snapshot.step.ts.md)
- [tools/core/steps/source.step.ts](https://banes-lab.com/source/coordination/tools/core/steps/source.step.ts.md)
- [tools/core/steps/typecheck.step.ts](https://banes-lab.com/source/coordination/tools/core/steps/typecheck.step.ts.md)

## Source

```typescript
import type { Finding, Verdict } from "../types/segment.types.ts";
import { JOIN_ABANDONED, joinedResult } from "../strings/pipeline.strings.ts";

import { decodeScope, encodeScope } from "../transformers/scope.transformer.ts";
import { dirname, join } from "node:path";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { GENERATED_DIR } from "../constants/path.constants.ts";
import type { Stage } from "../types/rule.types.ts";

export interface RuleReport {
    readonly rule: string;
    readonly mechanism?: string;
    readonly stage: Stage;
    readonly invariant: string;
    readonly verdict: Verdict;
    readonly scope: string;
    readonly authoritative: boolean;
    readonly scanned: number;
    readonly healed: readonly string[];
    readonly findings: readonly Finding[];
    readonly derivations?: unknown;
}

export interface StageSummary {
    readonly stage: Stage;
    readonly rule: string;
    readonly invariant: string;
    readonly findings: number;
    readonly healed: number;
    readonly bypassed: boolean;
}

export interface PipelineReport {
    readonly tool: "govern";
    readonly verdict: Verdict;
    readonly registered: number;
    readonly agent: string;
    readonly at: number;
    readonly scope: string;
    readonly authoritative: boolean;
    readonly scanned: number;
    readonly bypassed: boolean;
    readonly mutated: boolean;
    readonly mode: "healing" | "held";
    readonly written: readonly string[];
    readonly escaped: readonly string[];
    readonly unfulfilled: readonly string[];
    readonly stages: readonly StageSummary[];
    readonly reports: readonly string[];
    readonly findings: readonly Finding[];
    readonly moved: readonly string[];
    readonly derivations: Record<string, unknown>;
}

const write = function write(repoRoot: string, name: string, body: unknown): string {
    const target = join(repoRoot, GENERATED_DIR, name);
    mkdirSync(dirname(target), { recursive: true });
    writeFileSync(target, `${JSON.stringify(body, null, 2)}\n`, "utf8");
    return target;
};

export const ruleReportName = function ruleReportName(ruleId: string): string {
    return `${ruleId}.report.generated.json`;
};

export const writeRuleReport = function writeRuleReport(repoRoot: string, ruleId: string, report: RuleReport): string {
    return write(repoRoot, ruleReportName(ruleId), report);
};

export const AGGREGATE_REPORT = "pipeline.report.generated.json";

const CHANNEL_LEAD = "pipeline.";

const CHANNEL_TAIL = ".report.generated.json";

export const channelReportName = function channelReportName(scope: string): string {
    return `${CHANNEL_LEAD}${encodeScope(scope)}${CHANNEL_TAIL}`;
};

export const channelScopeOf = function channelScopeOf(name: string): string | null {
    if (!name.startsWith(CHANNEL_LEAD) || !name.endsWith(CHANNEL_TAIL)) {
        return null;
    }

    const encoded = name.slice(CHANNEL_LEAD.length, name.length - CHANNEL_TAIL.length);
    if (encoded.length === 0) {
        return null;
    }

    return decodeScope(encoded);
};

export const supersedingRun = function supersedingRun(repoRoot: string, at: number): number {
    const target = join(repoRoot, GENERATED_DIR, AGGREGATE_REPORT);
    if (!existsSync(target)) {
        return 0;
    }

    let parsed: unknown;
    try {
        parsed = JSON.parse(readFileSync(target, "utf8"));
    } catch {
        return 0;
    }

    if (typeof parsed !== "object" || parsed === null) {
        return 0;
    }

    const stamp = (parsed as Record<string, unknown>)["at"];
    if (typeof stamp !== "number" || stamp <= at) {
        return 0;
    }

    return stamp;
};

const JOIN_POLL_MS = 500;

const JOIN_LIMIT_MS = 600_000;

const publishedAfter = function publishedAfter(repoRoot: string, at: number): Record<string, unknown> | null {
    const target = join(repoRoot, GENERATED_DIR, AGGREGATE_REPORT);
    if (!existsSync(target)) {
        return null;
    }

    let parsed: unknown;
    try {
        parsed = JSON.parse(readFileSync(target, "utf8"));
    } catch {
        return null;
    }

    if (typeof parsed !== "object" || parsed === null) {
        return null;
    }

    const record = parsed as Record<string, unknown>;
    const stamp = record["at"];

    return typeof stamp === "number" && stamp >= at ? record : null;
};

export const joinLiveRun = async function joinLiveRun(
    repoRoot: string,
    at: number,
): Promise<{ message: string; code: number }> {
    const deadline = at + JOIN_LIMIT_MS;

    for (;;) {
        const published = publishedAfter(repoRoot, at);

        if (published !== null) {
            const verdict = typeof published["verdict"] === "string" ? published["verdict"] : "unknown";
            const agent = typeof published["agent"] === "string" ? published["agent"] : "an undeclared caller";
            const scope = typeof published["scope"] === "string" ? published["scope"] : "unknown";
            const findings = Array.isArray(published["findings"]) ? published["findings"].length : 0;

            return {
                code: verdict === "pass" ? 0 : 1,
                message: joinedResult({ agent, findings, scope, verdict }, AGGREGATE_REPORT),
            };
        }

        if (Date.now() > deadline) {
            return { code: 2, message: JOIN_ABANDONED };
        }

        await new Promise((resolve) => setTimeout(resolve, JOIN_POLL_MS));
    }
};

export const writePipelineReport = function writePipelineReport(
    repoRoot: string,
    report: PipelineReport,
): string | null {
    if (!report.authoritative) {
        return null;
    }
    if (supersedingRun(repoRoot, report.at) !== 0) {
        return null;
    }

    return write(repoRoot, AGGREGATE_REPORT, report);
};
```
