← Files AixcutARCHIVED FILE

scripts/workflow-evaluation.mjs

2.63 KB · Nov 6, 2026 · 07:28 UTC

↓ Download file

const mutationEvents = events => events.filter(event => event?.type === 'mutation');
const ratio = (numerator, denominator) => denominator ? numerator / denominator : null;

/** Offline trace scorer. It reports null when a trace omitted a measurement. */
export function evaluateWorkflow(trace) {
  const events = Array.isArray(trace?.events) ? trace.events : [];
  const mutations = mutationEvents(events);
  const ids = new Set();
  let duplicateMutations = 0;
  let preserveChecks = 0;
  let preserveViolations = 0;
  let readBacks = 0;
  for (const mutation of mutations) {
    const id = mutation.mutation_id || mutation.idempotency_key || mutation.operation_id;
    if (id) { if (ids.has(id)) duplicateMutations++; ids.add(id); }
    const preserve = Array.isArray(mutation.preserve) ? mutation.preserve : [];
    // A trace that only records the requested scope has not observed whether
    // the preserved object changed. Keep that metric unmeasured instead of
    // turning missing before/after evidence into a false zero violation.
    if (preserve.length && mutation.before !== undefined && mutation.after !== undefined) {
      preserveChecks++;
      if (JSON.stringify(mutation.before ?? {}) !== JSON.stringify(mutation.after ?? {})) preserveViolations++;
    }
    if (events.some(event => event.type === 'read_back' && event.revision === mutation.revision_after)) readBacks++;
  }
  const unknown = events.filter(event => event.type === 'unknown' && (event.mutation_id || event.operation_id || event.job_id));
  const recovered = new Set(events.filter(event => ['recovered','receipt','job_poll'].includes(event.type) && ['applied','duplicate','noop','rejected'].includes(event.status)).map(event => event.mutation_id || event.operation_id || event.job_id));
  const visualObservations = events.filter(event => event.type === 'evidence_collected' && (event.layer === 'visual' || event.visual === true));
  return {
    scenario: trace?.scenario,
    host: trace?.host,
    variant: trace?.variant,
    real_benchmark: trace?.synthetic !== true && trace?.source === 'live',
    metrics: {
      plan_coverage: ratio(mutations.filter(event => typeof event.plan_id === 'string').length, mutations.length) ?? 0,
      preserve_violation_rate: ratio(preserveViolations, preserveChecks),
      duplicate_mutations: duplicateMutations,
      read_back_coverage: ratio(readBacks, mutations.length),
      unknown_recovery_rate: ratio(unknown.filter(event => recovered.has(event.mutation_id || event.operation_id || event.job_id)).length, unknown.length),
      visual_evidence_coverage: visualObservations.length ? ratio(visualObservations.length, mutations.length) : null,
    },
  };
}

SHA-256: 8417567c38234b376f632ca5eb82f6e7b1357985d0005a23ed585ebe199cc663