feat(planning): explain and trace planning decisions

This commit is contained in:
nessar committed 2026-08-31 00:02:45 +02:00
1 parent 609b3dece9
commit d7a13b0b1e
24 files changed
+6073 -342

No files matched your search

+66 -15
View File
@@ -1,41 +1,92 @@
/// <reference lib="webworker" />
import type { PlanningOutcome, PlanOutcome } from './leave-planner.service';
import { calculatePlanningInternal } from './leave-planner.core';
import type {
CounterfactualAnalysis,
LeavePlacement,
PlanOutcome,
PlanningOutcome,
PlanningSnapshot,
PlanningTraceState,
} from './leave-planner.service';
import {
analyzeCounterfactualInternal,
calculatePlanningInternal,
type CounterfactualBaselineOptions,
type PlanningArtifacts,
} from './leave-planner.core';
type WorkerPlanningRequest = {
horizon?: { start?: unknown; end?: unknown };
balances?: unknown;
rules?: unknown;
identities?: unknown;
labels?: unknown;
deadlineMs?: unknown;
progress?: unknown;
};
type WorkerRequest = {
type: 'plan';
request: WorkerPlanningRequest;
};
type WorkerRequest =
| {
type: 'plan';
request: WorkerPlanningRequest;
}
| {
type: 'counterfactual';
request: PlanningSnapshot;
plan: PlanOutcome;
placement: LeavePlacement;
state?: PlanningTraceState;
options?: CounterfactualBaselineOptions;
};
type WorkerResponse =
| { type: 'progress'; stage: string }
| { type: 'trace-overhead'; totalMs: number }
| { type: 'checkpoint'; plan: PlanOutcome }
| { type: 'result'; outcome: PlanningOutcome }
| { type: 'artifacts'; artifacts: PlanningArtifacts }
| { type: 'counterfactual-result'; analysis: CounterfactualAnalysis }
| { type: 'error'; message: string };
const worker = self as DedicatedWorkerGlobalScope;
worker.onmessage = (event: MessageEvent<WorkerRequest>) => {
if (event.data?.type !== 'plan') return;
try {
const outcome = calculatePlanningInternal(
{
...event.data.request,
progress: (stage: string) =>
worker.postMessage({ type: 'progress', stage } satisfies WorkerResponse),
},
(plan) => worker.postMessage({ type: 'checkpoint', plan } satisfies WorkerResponse),
if (event.data?.type === 'plan') {
let artifacts: PlanningArtifacts = {};
let outcomePosted = false;
const outcome = calculatePlanningInternal(
{
...event.data.request,
progress: (stage: string) =>
worker.postMessage({ type: 'progress', stage } satisfies WorkerResponse),
traceProgress: (totalMs: number) =>
worker.postMessage({
type: 'trace-overhead',
totalMs,
} satisfies WorkerResponse),
},
(plan) => worker.postMessage({ type: 'checkpoint', plan } satisfies WorkerResponse),
(value) => (artifacts = value),
(value) => {
outcomePosted = true;
worker.postMessage({ type: 'result', outcome: value } satisfies WorkerResponse);
},
);
if (!outcomePosted)
worker.postMessage({ type: 'result', outcome } satisfies WorkerResponse);
worker.postMessage({ type: 'artifacts', artifacts } satisfies WorkerResponse);
return;
}
if (event.data?.type !== 'counterfactual') return;
const analysis = analyzeCounterfactualInternal(
event.data.request,
event.data.plan,
event.data.placement,
event.data.state,
event.data.options,
);
worker.postMessage({ type: 'result', outcome } satisfies WorkerResponse);
worker.postMessage({ type: 'counterfactual-result', analysis } satisfies WorkerResponse);
} catch (error) {
worker.postMessage({
type: 'error',