/*
RFC-1297: leitstand.state.reconcile — record the observed live release
of one channel into system-state.yaml (state "reconciled") and append a
live-state-reconciled Bordbuch event. It is the sanctioned, operator-driven
remedy for LIVE-01 drift: reality is recorded deliberately, with a reason —
never auto-legitimized.
- Not a contract source of truth — callers depend on the module's exported API, not this file's internals.
- RFC-1297: initial — --site/--channel/--from-live/--reason; probes build-identity.json live, writes lastPropagated state "reconciled", appends live-state-reconciled Bordbuch event.
- RFC-1297
*/
import type {
KernelCommandInput,
KernelCommandResult,
KernelRuntimeContext,
} from "@warpgogol/werkstatt-engine/kernel";
import { generateOperationId } from "../werkstatt/index.ts";
import {
readSystemConfigSmart,
readSystemStateSmart,
writeSystemStateSmart,
} from "../sternsystem/registry-io.ts";
import {
buildAuthHeader,
fetchChannelBuildIdentity,
type LiveChannelName,
} from "../sternsystem/live-drift.ts";
import { appendAndCommitBordbuch } from "../bordbuch/bordbuch-commit-helper.ts";
import { flagSite } from "./deploy-helpers.ts";
const RECONCILE_CHANNELS: readonly LiveChannelName[] = ["dev", "alt", "main"];
export interface LeitstandStateReconcileData {
command: "leitstand.state.reconcile";
systemId: string;
channel: LiveChannelName;
recordedReleaseId: string | null;
liveReleaseId: string | null;
outcome: "reconciled" | "already-consistent";
reason: string;
operationId: string;
}
function reconcileResult(
data: LeitstandStateReconcileData,
summary: string,
exitCode: 0 | 1,
): KernelCommandResult {
return { data, summary, exitCode };
}
export async function runLeitstandStateReconcile(
input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const systemId = flagSite(input);
if (!systemId) throw new Error("[leitstand.state.reconcile] --site is required");
const channelFlag = input.flags["channel"];
if (typeof channelFlag !== "string" || !RECONCILE_CHANNELS.includes(channelFlag as never)) {
throw new Error("[leitstand.state.reconcile] --channel dev|alt|main is required");
}
const channel = channelFlag as LiveChannelName;
if (input.flags["from-live"] !== true) {
throw new Error(
"[leitstand.state.reconcile] --from-live is required — the only supported source is the live build-identity.json",
);
}
const reasonFlag = input.flags["reason"];
const reason = typeof reasonFlag === "string" ? reasonFlag.trim() : "";
if (reason.length < 10) {
throw new Error(
"[leitstand.state.reconcile] --reason is required (>= 10 chars) — recording reality needs an accountable justification",
);
}
const base: LeitstandStateReconcileData = {
command: "leitstand.state.reconcile",
systemId,
channel,
recordedReleaseId: null,
liveReleaseId: null,
outcome: "reconciled",
reason,
operationId: generateOperationId(),
};
const systemConfig = await readSystemConfigSmart(context.workspaceRoot, systemId, context.io);
const channelUrl = systemConfig.deployment?.channels?.[channel]?.url;
if (!channelUrl) {
return reconcileResult(
base,
`[leitstand.state.reconcile] ${systemId}: channel '${channel}' has no configured URL`,
1,
);
}
const systemState = await readSystemStateSmart(context.workspaceRoot, systemId, context.io);
const recordedReleaseId = systemState.lastPropagated?.[channel]?.releaseId ?? null;
base.recordedReleaseId = recordedReleaseId;
// RFC-1188: same outbound signing seam as leitstand.verify — channel probes
// are platform-originated agent traffic.
const { resolveOutboundAgentClient } = await import("./wba-signer.ts");
const outbound = await resolveOutboundAgentClient(
context.workspaceRoot,
systemId,
undefined,
{
onSigningFailure: (e) =>
context.logger.warn(
`[leitstand.state.reconcile] outbound signing failed, request sent unsigned: ${e instanceof Error ? e.message : String(e)}`,
),
},
);
const result = await fetchChannelBuildIdentity(
channel,
channelUrl,
buildAuthHeader(systemState.accessPin),
10_000,
outbound.client.fetch,
);
const liveReleaseId = result.buildIdentity?.releaseId ?? null;
base.liveReleaseId = liveReleaseId;
if (liveReleaseId === null) {
// A missing identity is a deployment problem, not a records problem.
return reconcileResult(
base,
`[leitstand.state.reconcile] ${systemId}: ${channel} live identity unreadable (${result.error ?? "no build-identity"}) — nothing truthful to record`,
1,
);
}
if (liveReleaseId === recordedReleaseId) {
base.outcome = "already-consistent";
return reconcileResult(
base,
`[leitstand.state.reconcile] ${systemId}: ${channel} already consistent — live and recorded both ${liveReleaseId}`,
0,
);
}
systemState.lastPropagated[channel] = {
releaseId: liveReleaseId,
at: new Date().toISOString(),
healthy: null,
state: "reconciled",
operationId: base.operationId,
leaseExpiresAt: null,
};
await writeSystemStateSmart(context.workspaceRoot, systemId, systemState, context.io);
await appendAndCommitBordbuch(
context.workspaceRoot,
systemId,
"live-state-reconciled",
`${channel}: recorded ${recordedReleaseId ?? "(none)"} → live ${liveReleaseId}: ${reason}`,
"leitstand",
{ releaseId: liveReleaseId, metadata: { channel, operationId: base.operationId, reason } },
);
return reconcileResult(
base,
`[leitstand.state.reconcile] ${systemId}: ${channel} reconciled — recorded ${recordedReleaseId ?? "(none)"} → live ${liveReleaseId}`,
0,
);
}