refactor(planning): remove the original decision trace
CI / policy (push) Successful in 7s
CI / validate (push) Successful in 1m15s
CI / test (push) Successful in 3m18s
CI / benchmark (push) Failing after 5m53s
CI / build (push) Successful in 1m23s
CI / security (push) Successful in 7m6s
CI / policy (pull_request) Successful in 7s
CI / validate (pull_request) Successful in 1m9s
CI / build (pull_request) Successful in 43s
CI / test (pull_request) Successful in 3m19s
CI / images (push) Successful in 3m7s
CI / required (push) Successful in 2s
CI / benchmark (pull_request) Failing after 5m28s
CI / security (pull_request) Successful in 6m37s
CI / images (pull_request) Successful in 3m18s
CI / required (pull_request) Successful in 1s

This commit is contained in:
nessar committed 2026-10-15 20:42:33 +02:00
1 parent ea4256107f
commit d9d9befa12
43 files changed
+729 -8880

No files matched your search

+7 -272
View File
@@ -7,18 +7,13 @@ import type {
PlanningExplanation,
PlanningOutcome,
PlanningReason,
PlanningTrace,
PlanningTraceUpdate,
PlanningSnapshot,
PlanningTraceState,
TraceCandidateStatus,
PlanningStateSnapshot,
} from './leave-planner.service';
import {
analyzeCounterfactualInternal,
calculatePlanningAsyncInternal,
explainIncompleteCheckpoint,
historicalCounterfactualReasons,
type CounterfactualBaselineOptions,
type PlanningArtifacts,
} from './leave-planner.core';
@@ -39,271 +34,52 @@ type WorkerRequest =
type: 'plan';
request: WorkerPlanningRequest;
}
| { type: 'trace-ack'; count: number }
| { type: 'cancel' }
| {
type: 'counterfactual';
request: PlanningSnapshot;
plan: PlanOutcome;
placement: LeavePlacement;
state?: PlanningTraceState;
options?: CounterfactualBaselineOptions;
}
| {
type: 'historical-eligibility';
request: PlanningSnapshot;
placement: LeavePlacement;
state: PlanningTraceState;
options?: CounterfactualBaselineOptions;
state?: PlanningStateSnapshot;
}
| {
type: 'explain-checkpoint';
request: PlanningSnapshot;
plan: PlanOutcome;
state: PlanningTraceState;
state: PlanningStateSnapshot;
interruption: PlanningReason;
};
type WorkerResponse =
| { type: 'heartbeat' }
| { type: 'progress'; stage: string }
| { type: 'trace-overhead'; totalMs: number }
| { type: 'checkpoint'; plan: PlanOutcome; state?: PlanningTraceState }
| { type: 'checkpoint'; plan: PlanOutcome; state?: PlanningStateSnapshot }
| { type: 'result'; outcome: PlanningOutcome }
| { type: 'artifacts'; artifacts: PlanningArtifacts }
| {
type: 'explanation';
explanation?: PlanningExplanation;
explanationError?: PlanningReason;
finalState?: PlanningTraceState;
finalState?: PlanningStateSnapshot;
}
| {
type: 'artifacts-start';
artifacts: Omit<
PlanningArtifacts,
'trace' | 'explanation' | 'explanationError' | 'finalState'
>;
trace?: Omit<PlanningTrace, 'phases'>;
}
| {
type: 'trace-update';
totalMs: number;
update: PlanningTraceUpdate;
acknowledge?: true;
}
| {
type: 'trace-status-index';
statuses: ReadonlyMap<string, TraceCandidateStatus>;
nonWorkingDates: readonly string[];
}
| { type: 'artifacts-complete'; traceTransferMs: number }
| { type: 'counterfactual-result'; analysis: CounterfactualAnalysis }
| {
type: 'historical-eligibility-result';
entries: ReturnType<typeof historicalCounterfactualReasons>;
}
| { type: 'error'; code?: string; message: string };
const worker = self as DedicatedWorkerGlobalScope;
const TRACE_OPERATIONS_PER_MESSAGE = 64;
const MAX_TRACE_PACKETS_IN_FLIGHT = 16;
interface StagedTraceUpdate {
totalMs: number;
update?: PlanningTraceUpdate;
nextOperation: number;
}
interface TraceDelivery {
updates: StagedTraceUpdate[];
nextUpdate: number;
inFlight: number;
traceTransferMs: number;
artifacts?: PlanningArtifacts;
calculationComplete: boolean;
}
let activeTraceDelivery: TraceDelivery | undefined;
let activeCalculation: { cancelled: boolean } | undefined;
function traceStatusIndex(trace: PlanningTrace): {
statuses: Map<string, TraceCandidateStatus>;
nonWorkingDates: string[];
} {
const priority: Record<TraceCandidateStatus, number> = {
ineligible: 0,
'not-examined': 1,
pruned: 2,
'evaluated-rejected': 3,
chosen: 4,
};
const statuses = new Map<string, TraceCandidateStatus>();
const nonWorkingDates = new Set(trace.nonWorkingDates?.map(({ date }) => date) ?? []);
for (const phase of trace.phases) {
for (const decision of phase.decisions) {
for (const candidate of decision.candidates) {
const dates = candidate.dates ?? (candidate.date ? [candidate.date] : []);
for (const date of dates) {
const previous = statuses.get(date);
if (!previous || priority[candidate.status] > priority[previous])
statuses.set(date, candidate.status);
}
}
}
}
return { statuses, nonWorkingDates: [...nonWorkingDates] };
}
function sendArtifacts(artifacts: PlanningArtifacts, deferredTraceTransferMs = 0): void {
const trace = artifacts.trace;
let traceMaterializationMs = artifacts.timings?.traceMaterializationMs ?? 0;
let traceTransferMs = (artifacts.timings?.traceTransferMs ?? 0) + deferredTraceTransferMs;
if (trace) {
const materializationStarted = performance.now();
const statusIndex = traceStatusIndex(trace);
traceMaterializationMs += Math.max(0, performance.now() - materializationStarted);
const transferStarted = performance.now();
worker.postMessage({
type: 'trace-status-index',
statuses: statusIndex.statuses,
nonWorkingDates: statusIndex.nonWorkingDates,
} satisfies WorkerResponse);
traceTransferMs += Math.max(0, performance.now() - transferStarted);
}
const rest: Omit<
PlanningArtifacts,
'trace' | 'explanation' | 'explanationError' | 'finalState'
> = artifacts.timings
? {
timings: {
...artifacts.timings,
traceMaterializationMs,
traceTransferMs,
},
}
: {};
const transferStarted = performance.now();
worker.postMessage({
type: 'artifacts-start',
artifacts: rest,
trace: trace
? {
status: trace.status,
initialState: trace.initialState,
completedStages: trace.completedStages,
nonWorkingDates: trace.nonWorkingDates,
error: trace.error,
}
: undefined,
} satisfies WorkerResponse);
traceTransferMs += Math.max(0, performance.now() - transferStarted);
// Capture updates already carried every candidate in bounded operations.
// The final packet closes the trace without cloning it a second time.
worker.postMessage({ type: 'artifacts-complete', traceTransferMs } satisfies WorkerResponse);
}
function finishTraceDelivery(delivery: TraceDelivery): void {
if (
!delivery.calculationComplete ||
delivery.nextUpdate < delivery.updates.length ||
delivery.inFlight > 0 ||
!delivery.artifacts
)
return;
if (activeTraceDelivery === delivery) activeTraceDelivery = undefined;
try {
sendArtifacts(delivery.artifacts, delivery.traceTransferMs);
} catch (error) {
worker.postMessage({
type: 'error',
code: 'trace-error',
message: error instanceof Error ? error.message : String(error),
} satisfies WorkerResponse);
}
delivery.updates = [];
}
function pumpTraceUpdates(delivery: TraceDelivery): void {
while (
delivery.inFlight < MAX_TRACE_PACKETS_IN_FLIGHT &&
delivery.nextUpdate < delivery.updates.length
) {
const staged = delivery.updates[delivery.nextUpdate];
const update = staged.update;
if (!update) {
delivery.nextUpdate++;
continue;
}
const start = staged.nextOperation;
const end = Math.min(start + TRACE_OPERATIONS_PER_MESSAGE, update.operations.length);
const firstPacket = start === 0;
const packet: WorkerResponse = {
type: 'trace-update',
totalMs: staged.totalMs,
acknowledge: true,
update: {
initialState: firstPacket ? update.initialState : undefined,
nonWorkingDates: firstPacket ? update.nonWorkingDates : undefined,
operations: update.operations.slice(start, end),
},
};
const transferStarted = performance.now();
try {
worker.postMessage(packet satisfies WorkerResponse);
} catch (error) {
if (activeTraceDelivery === delivery) activeTraceDelivery = undefined;
delivery.updates = [];
worker.postMessage({
type: 'error',
code: 'trace-error',
message: error instanceof Error ? error.message : String(error),
} satisfies WorkerResponse);
return;
}
delivery.traceTransferMs += Math.max(0, performance.now() - transferStarted);
delivery.inFlight++;
staged.nextOperation = end;
if (update.operations.length === 0 || end >= update.operations.length) {
// Drop the full drain array as soon as its last bounded packet is posted.
staged.update = undefined;
delivery.nextUpdate++;
}
}
if (delivery.nextUpdate === delivery.updates.length) {
// All queued operation arrays are released; only in-flight structured clones remain.
delivery.updates = [];
delivery.nextUpdate = 0;
}
finishTraceDelivery(delivery);
}
worker.onmessage = async (event: MessageEvent<WorkerRequest>) => {
try {
if (event.data?.type === 'cancel') {
if (activeCalculation) activeCalculation.cancelled = true;
return;
}
if (event.data?.type === 'trace-ack') {
const delivery = activeTraceDelivery;
if (!delivery || !Number.isInteger(event.data.count) || event.data.count <= 0) return;
delivery.inFlight = Math.max(0, delivery.inFlight - event.data.count);
pumpTraceUpdates(delivery);
return;
}
if (event.data?.type === 'plan') {
const calculation = { cancelled: false };
activeCalculation = calculation;
let artifacts: PlanningArtifacts = {};
let outcomePosted = false;
let outcome: PlanningOutcome;
const traceDelivery: TraceDelivery = {
updates: [],
nextUpdate: 0,
inFlight: 0,
traceTransferMs: 0,
calculationComplete: false,
};
activeTraceDelivery = traceDelivery;
// The cooperative driver yields during preparation and search, even within one phase.
const heartbeat = () =>
worker.postMessage({ type: 'heartbeat' } satisfies WorkerResponse);
@@ -318,13 +94,6 @@ worker.onmessage = async (event: MessageEvent<WorkerRequest>) => {
type: 'progress',
stage,
} satisfies WorkerResponse),
traceProgress: (totalMs: number, update: PlanningTraceUpdate) => {
worker.postMessage({
type: 'trace-overhead',
totalMs,
} satisfies WorkerResponse);
traceDelivery.updates.push({ totalMs, update, nextOperation: 0 });
},
},
(plan, state) =>
worker.postMessage({
@@ -350,8 +119,6 @@ worker.onmessage = async (event: MessageEvent<WorkerRequest>) => {
() => calculation.cancelled,
);
} catch (error) {
if (activeTraceDelivery === traceDelivery) activeTraceDelivery = undefined;
traceDelivery.updates = [];
worker.postMessage({
type: 'error',
code: 'planner-error',
@@ -364,9 +131,7 @@ worker.onmessage = async (event: MessageEvent<WorkerRequest>) => {
}
if (!outcomePosted)
worker.postMessage({ type: 'result', outcome } satisfies WorkerResponse);
traceDelivery.artifacts = artifacts;
traceDelivery.calculationComplete = true;
pumpTraceUpdates(traceDelivery);
worker.postMessage({ type: 'artifacts', artifacts } satisfies WorkerResponse);
return;
}
if (event.data?.type === 'explain-checkpoint') {
@@ -379,44 +144,14 @@ worker.onmessage = async (event: MessageEvent<WorkerRequest>) => {
worker.postMessage({ type: 'explanation', ...result } satisfies WorkerResponse);
return;
}
if (event.data?.type === 'historical-eligibility') {
worker.postMessage({
type: 'historical-eligibility-result',
entries: historicalCounterfactualReasons(
event.data.request,
event.data.state,
event.data.placement,
event.data.options,
),
} satisfies WorkerResponse);
return;
}
if (event.data?.type !== 'counterfactual') return;
try {
let analysis = analyzeCounterfactualInternal(
const analysis = analyzeCounterfactualInternal(
event.data.request,
event.data.plan,
event.data.placement,
event.data.state,
event.data.options,
);
if (event.data.options && event.data.state) {
const historicalReasons = new Map(
historicalCounterfactualReasons(
event.data.request,
event.data.state,
event.data.placement,
event.data.options,
).map(({ date, reason }) => [date, reason]),
);
analysis = {
...analysis,
results: analysis.results.map((result) => {
const historicalReason = historicalReasons.get(result.date);
return historicalReason ? { ...result, historicalReason } : result;
}),
};
}
worker.postMessage({
type: 'counterfactual-result',
analysis,