/*
RFC-1037: Thin kernel command handlers for effect.classify,
effect.compensation.verify, and effect.compensation.inspect commands.
Each handler delegates to pure functions in effect-classifier.ts and
compensation-verifier.ts and wraps the result in KernelCommandResult.
- Does not implement classification logic — that lives in effect-classifier.ts.
- Does not implement probe execution — that lives in compensation-verifier.ts.
- Does not register commands — registration lives in effects.module.ts.
- RFC-1037: initial implementation — 3 effect command handlers.
- RFC-1249: wire werkstatt.commands.validate into packages.check + retire CMD-OUTPUT debt
*/
import type {
KernelCommandInput,
KernelCommandResult,
KernelRuntimeContext,
} from "@warpgogol/werkstatt-shared/kernel";
import type { EffectClass, CompensationResult } from "@warpgogol/werkstatt-shared/component";
import type { Sha256Digest } from "@warpgogol/werkstatt-shared/fingerprint";
import { isSha256Digest } from "@warpgogol/werkstatt-shared/fingerprint";
import { getDefaultEffectClassifier } from "./effect-classifier.ts";
import { CompensationVerifier } from "./compensation-verifier.ts";
import { CompensationStore, resolveCompensationStorePath } from "./compensation-store.ts";
export interface ClassifyResult {
operation: string;
effectClass: EffectClass;
}
export function runEffectClassify(
input: KernelCommandInput,
_context: KernelRuntimeContext,
): KernelCommandResult {
const operation = input.flags["operation"] as string | undefined;
if (!operation) {
throw new Error("EFFECT-CMD-01: --operation flag is required");
}
const classifier = getDefaultEffectClassifier();
const effectClass = classifier.classify(operation);
return {
data: { operation, effectClass },
exitCode: 0,
summary: `[effect.commands] Operation "${operation}" classified as ${effectClass}`,
};
}
export async function runEffectCompensationVerify(
input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const operationHash = input.flags["operation-hash"] as string | undefined;
const compensationHash = input.flags["compensation-hash"] as string | undefined;
if (!operationHash || !isSha256Digest(operationHash)) {
throw new Error("EFFECT-CMD-02: --operation-hash flag (sha256:...) is required");
}
if (!compensationHash || !isSha256Digest(compensationHash)) {
throw new Error("EFFECT-CMD-03: --compensation-hash flag (sha256:...) is required");
}
const operation = input.flags["operation"] as string | undefined;
if (!operation) {
throw new Error("EFFECT-CMD-04: --operation flag is required to resolve compensation action");
}
const classifier = getDefaultEffectClassifier();
const decl = classifier.getDeclaration(operation);
if (!decl) {
throw new Error(`EFFECT-03: operation "${operation}" has no effect class declaration`);
}
if (decl.effectClass === "irreversible-emission") {
throw new Error("EFFECT-01: irreversible emissions cannot be compensated");
}
if (!decl.compensation) {
throw new Error("EFFECT-02: compensatable operation requires a compensation action");
}
const workspaceRoot =
(context as unknown as { workspaceRoot?: string }).workspaceRoot ?? process.cwd();
const missionId = input.flags["mission-id"] as string | undefined;
const missionsDir =
(input.flags["missions-dir"] as string | undefined) ?? `${workspaceRoot}/missions`;
const storePath = missionId
? resolveCompensationStorePath(missionsDir, missionId)
: `${workspaceRoot}/missions/_global/compensation-evidence.db`;
const store = await CompensationStore.create(storePath, context.io);
const verifier = new CompensationVerifier(store, context);
return verifier
.verify(operationHash as Sha256Digest, compensationHash as Sha256Digest, decl.compensation)
.then((result) => {
store.close();
return {
data: result,
exitCode: result.verified ? 0 : 1,
summary: result.verified
? `[effect.commands] Compensation verified for ${operationHash}`
: `[effect.commands] Compensation verification failed for ${operationHash}: ${result.failureReason ?? "unknown"}`,
};
})
.catch((err) => {
store.close();
throw err;
});
}
export async function runEffectCompensationInspect(
input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const operationHash = input.flags["operation-hash"] as string | undefined;
if (!operationHash || !isSha256Digest(operationHash)) {
throw new Error("EFFECT-CMD-02: --operation-hash flag (sha256:...) is required");
}
const workspaceRoot =
(context as unknown as { workspaceRoot?: string }).workspaceRoot ?? process.cwd();
const missionId = input.flags["mission-id"] as string | undefined;
const missionsDir =
(input.flags["missions-dir"] as string | undefined) ?? `${workspaceRoot}/missions`;
const storePath = missionId
? resolveCompensationStorePath(missionsDir, missionId)
: `${workspaceRoot}/missions/_global/compensation-evidence.db`;
const store = await CompensationStore.create(storePath, context.io);
const result = store.get(operationHash as Sha256Digest);
store.close();
return {
data: result,
exitCode: 0,
summary: result
? `[effect.commands] Compensation evidence found for ${operationHash}: verified=${result.verified}`
: `[effect.commands] No compensation evidence found for ${operationHash}`,
};
}