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; } 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)["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 | 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; 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); };