Skip to main content
Pedro Alizo

Work sample / Source

Finance Variance Review Workbench

These are the source files used to build the interactive demo. The project uses invented data and has no model calls, external integrations, or production deployment.

Project notes Open demo

src/lib/variance-workbench.ts
export type ReviewStatus = "needs-review" | "reviewed" | "escalated";

export interface VarianceRecord {
  id: string;
  account: string;
  team: string;
  period: string;
  plan: number;
  actual: number;
  driver: string;
  sources: string[];
  status: ReviewStatus;
  reviewNote?: string;
}

export const sampleVarianceRecords: VarianceRecord[] = [
  {
    id: "SYN-1042",
    account: "Vendor services",
    team: "Operations",
    period: "Sep 2026",
    plan: 42000,
    actual: 54600,
    driver:
      "Additional service days were included in the synthetic activity detail.",
    sources: ["SYN-GL-1042", "SYN-ACT-308"],
    status: "needs-review",
  },
  {
    id: "SYN-1187",
    account: "Customer support",
    team: "Product",
    period: "Sep 2026",
    plan: 31500,
    actual: 28350,
    driver:
      "One planned delivery moved into the next period in the synthetic activity detail.",
    sources: ["SYN-GL-1187", "SYN-ACT-326"],
    status: "needs-review",
  },
  {
    id: "SYN-1214",
    account: "Travel and expenses",
    team: "Shared services",
    period: "Sep 2026",
    plan: 18000,
    actual: 23400,
    driver:
      "The synthetic activity detail shows higher travel volume than planned.",
    sources: ["SYN-GL-1214", "SYN-ACT-341"],
    status: "needs-review",
  },
];

export interface Variance {
  amount: number;
  percentage: number | null;
  direction: "over" | "under" | "on-plan";
}

export function calculateVariance(plan: number, actual: number): Variance {
  const amount = actual - plan;
  const percentage = plan === 0 ? null : (amount / Math.abs(plan)) * 100;

  return {
    amount,
    percentage,
    direction: amount > 0 ? "over" : amount < 0 ? "under" : "on-plan",
  };
}

export function updateReviewStatus(
  records: VarianceRecord[],
  recordId: string,
  status: ReviewStatus,
  reviewNote?: string,
): VarianceRecord[] {
  let found = false;
  const updated = records.map((record) => {
    if (record.id !== recordId) return record;
    found = true;
    return { ...record, status, reviewNote };
  });

  if (!found) throw new Error(`Unknown variance record: ${recordId}`);
  return updated;
}
src/lib/variance-workbench.test.ts
import { describe, expect, it } from "vitest";
import {
  calculateVariance,
  sampleVarianceRecords,
  updateReviewStatus,
} from "./variance-workbench";

describe("finance variance workbench", () => {
  it("calculates positive and negative variances from plan", () => {
    expect(calculateVariance(42000, 54600)).toEqual({
      amount: 12600,
      percentage: 30,
      direction: "over",
    });
    expect(calculateVariance(31500, 28350)).toEqual({
      amount: -3150,
      percentage: -10,
      direction: "under",
    });
  });

  it("does not invent a percentage when the plan is zero", () => {
    expect(calculateVariance(0, 250)).toEqual({
      amount: 250,
      percentage: null,
      direction: "over",
    });
  });

  it("updates one record without mutating the original list", () => {
    const updated = updateReviewStatus(
      sampleVarianceRecords,
      "SYN-1042",
      "reviewed",
      "Checked against the synthetic activity detail.",
    );

    expect(updated[0]).toMatchObject({
      status: "reviewed",
      reviewNote: "Checked against the synthetic activity detail.",
    });
    expect(sampleVarianceRecords[0].status).toBe("needs-review");
    expect(updated[1]).toBe(sampleVarianceRecords[1]);
  });

  it("rejects status updates for records outside the fixture", () => {
    expect(() =>
      updateReviewStatus(sampleVarianceRecords, "REAL-001", "escalated"),
    ).toThrow("Unknown variance record: REAL-001");
  });
});
src/pages/lab/finance-variance-workbench.astro
---
import Section from "@/components/common/Section.astro";
import PageHead from "@/components/seo/PageHead.astro";
import Layout from "@/layouts/Layout.astro";
import {
  calculateVariance,
  sampleVarianceRecords,
} from "@/lib/variance-workbench";
import {
  getDefaultOgPathOrUrl,
  resolveOgImageUrl,
} from "@/lib/resolve-og-image";
import { getConfigurationCollection } from "@/lib/site-config";

const { data: config } = await getConfigurationCollection();
const ogImageUrl = resolveOgImageUrl({
  baseUrl: config.site.baseUrl,
  defaultOgPathOrUrl: getDefaultOgPathOrUrl(config),
});
const money = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});
const initialVariance = calculateVariance(
  sampleVarianceRecords[0].plan,
  sampleVarianceRecords[0].actual,
);
const formatSignedMoney = (amount: number) =>
  `${amount > 0 ? "+" : amount < 0 ? "−" : ""}${money.format(Math.abs(amount))}`;
---

<Layout>
  <Fragment slot="head">
    <PageHead
      title="Finance Variance Review Workbench — Pedro Alizo"
      description="A finance workflow prototype with synthetic data, source references, deterministic calculations, and human review controls."
      canonicalPath="/lab/finance-variance-workbench"
      baseUrl={config.site.baseUrl}
      ogImageUrl={ogImageUrl}
      keywords={["finance workflow", "synthetic data", "human review"]}
      includeLanguageMeta
      robots="noindex, follow"
    />
  </Fragment>
  <Section class="my-8 pt-8">
    <div class="mb-8 flex flex-col gap-4">
      <p class="font-mono text-sm uppercase zag-muted">Lab / Synthetic data</p>
      <h1 class="text-3xl sm:text-4xl page-title leading-tight font-semibold">
        Finance variance review
      </h1>
      <p class="max-w-prose">
        A small workbench for reviewing plan-to-actual exceptions, checking
        their source references, and recording a human decision.
      </p>
      <p class="max-w-prose text-sm zag-muted">
        Prototype boundary: every record is synthetic. Variances are calculated
        by deterministic code; the explanation is fixture text, not model
        output. There are no uploads, external requests, persistent storage, or
        accounting-system writebacks.
      </p>
    </div>

    <div
      data-variance-workbench
      class="zag-border zag-bg zag-text border-2 p-4 sm:p-6"
    >
      <header class="mb-6 flex flex-col gap-4 border-b zag-border pb-5 sm:flex-row sm:items-end sm:justify-between">
        <div>
          <p class="font-mono text-xs uppercase zag-muted">Sample close</p>
          <h2 class="mt-1 page-title text-xl font-semibold">
            September 2026 · Sample operating unit
          </h2>
        </div>
        <button type="button" class="zag-btn text-sm" data-reset-workbench>
          Reset sample
        </button>
      </header>

      <dl class="mb-6 grid grid-cols-2 gap-4 border-b zag-border pb-6 sm:grid-cols-4">
        <div>
          <dt class="text-xs zag-muted">Exceptions</dt>
          <dd class="mt-1 font-mono text-xl tabular-nums" data-count-total>
            {sampleVarianceRecords.length}
          </dd>
        </div>
        <div>
          <dt class="text-xs zag-muted">Needs review</dt>
          <dd
            class="mt-1 font-mono text-xl tabular-nums"
            data-count-needs-review
          >
            {sampleVarianceRecords.length}
          </dd>
        </div>
        <div>
          <dt class="text-xs zag-muted">Reviewed</dt>
          <dd class="mt-1 font-mono text-xl tabular-nums" data-count-reviewed>
            0
          </dd>
        </div>
        <div>
          <dt class="text-xs zag-muted">Net variance</dt>
          <dd class="mt-1 font-mono text-xl tabular-nums" data-net-variance>
            {formatSignedMoney(
              sampleVarianceRecords.reduce(
                (sum, record) => sum + record.actual - record.plan,
                0,
              ),
            )}
          </dd>
        </div>
      </dl>

      <div class="grid gap-8 lg:grid-cols-[minmax(15rem,0.8fr)_minmax(0,1.4fr)]">
        <section aria-labelledby="queue-heading">
          <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
            <h3 id="queue-heading" class="page-title text-lg font-semibold">
              Review queue
            </h3>
            <div
              class="flex flex-wrap gap-1"
              role="group"
              aria-label="Filter review queue"
              data-review-filters
            >
              <button
                type="button"
                class="zag-btn text-xs"
                data-filter="all"
                aria-pressed="true"
              >
                All
              </button>
              <button
                type="button"
                class="zag-btn text-xs"
                data-filter="needs-review"
                aria-pressed="false"
              >
                Needs review
              </button>
              <button
                type="button"
                class="zag-btn text-xs"
                data-filter="reviewed"
                aria-pressed="false"
              >
                Reviewed
              </button>
              <button
                type="button"
                class="zag-btn text-xs"
                data-filter="escalated"
                aria-pressed="false"
              >
                Escalated
              </button>
            </div>
          </div>
          <ul
            class="divide-y zag-border"
            data-record-list
            aria-label="Synthetic variance records"
          >
            {sampleVarianceRecords.map((record, index) => {
              const variance = calculateVariance(record.plan, record.actual);
              return (
                <li>
                  <button
                    type="button"
                    class="w-full py-4 text-left"
                    data-record-id={record.id}
                    aria-pressed={index === 0 ? "true" : "false"}
                  >
                    <span class="flex items-start justify-between gap-3">
                      <span class="min-w-0">
                        <span class="block font-medium">{record.account}</span>
                        <span class="mt-1 block text-xs zag-muted">
                          {record.team} · {record.id}
                        </span>
                      </span>
                      <span class="shrink-0 text-right font-mono text-sm tabular-nums">
                        {formatSignedMoney(variance.amount)}
                        <span class="mt-1 block text-xs zag-muted">
                          Needs review
                        </span>
                      </span>
                    </span>
                  </button>
                </li>
              );
            })}
          </ul>
          <p class="mt-4 text-xs zag-muted">
            Materiality is not inferred here. Reviewers decide what requires
            action.
          </p>
        </section>

        <section
          aria-labelledby="detail-heading"
          class="border-t zag-border pt-6 lg:border-l lg:border-t-0 lg:pl-8 lg:pt-0"
          data-record-detail
        >
          <div class="mb-5 flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between">
            <div>
              <p class="font-mono text-xs uppercase zag-muted" data-detail-id>
                {sampleVarianceRecords[0].id}
              </p>
              <h3
                id="detail-heading"
                class="mt-1 page-title text-xl font-semibold"
                data-detail-account
              >
                {sampleVarianceRecords[0].account}
              </h3>
              <p class="text-sm zag-muted" data-detail-team>
                {sampleVarianceRecords[0].team} ·{" "}
                {sampleVarianceRecords[0].period}
              </p>
            </div>
            <span
              class="mt-2 inline-flex w-fit border zag-border px-2 py-1 font-mono text-xs"
              data-detail-status
            >
              Needs review
            </span>
          </div>

          <dl class="mb-6 grid grid-cols-2 gap-x-4 gap-y-3 border-b zag-border pb-5 sm:grid-cols-4">
            <div>
              <dt class="text-xs zag-muted">Plan</dt>
              <dd class="mt-1 font-mono tabular-nums" data-detail-plan>
                {money.format(sampleVarianceRecords[0].plan)}
              </dd>
            </div>
            <div>
              <dt class="text-xs zag-muted">Actual</dt>
              <dd class="mt-1 font-mono tabular-nums" data-detail-actual>
                {money.format(sampleVarianceRecords[0].actual)}
              </dd>
            </div>
            <div>
              <dt class="text-xs zag-muted">Variance</dt>
              <dd class="mt-1 font-mono tabular-nums" data-detail-amount>
                {formatSignedMoney(initialVariance.amount)}
              </dd>
            </div>
            <div>
              <dt class="text-xs zag-muted">Against plan</dt>
              <dd class="mt-1 font-mono tabular-nums" data-detail-percentage>
                +{initialVariance.percentage}%
              </dd>
            </div>
          </dl>

          <div class="mb-6">
            <h4 class="text-xs font-mono uppercase zag-muted">
              Context in fixture
            </h4>
            <p class="mt-2 max-w-prose text-sm" data-detail-driver>
              {sampleVarianceRecords[0].driver}
            </p>
            <p class="mt-2 text-xs zag-muted">
              Rule-based context, not an AI-generated explanation.
            </p>
          </div>

          <div class="mb-6">
            <h4 class="text-xs font-mono uppercase zag-muted">
              Source references
            </h4>
            <ul class="mt-2 flex flex-wrap gap-2" data-detail-sources>
              {sampleVarianceRecords[0].sources.map((source) => (
                <li class="border zag-border px-2 py-1 font-mono text-xs">
                  {source}
                </li>
              ))}
            </ul>
            <p class="mt-2 text-xs zag-muted">
              IDs are synthetic fixtures; no source system is connected.
            </p>
          </div>

          <label
            for="review-note"
            class="block text-xs font-mono uppercase zag-muted"
          >
            Reviewer note
          </label>
          <textarea
            id="review-note"
            class="zag-input mt-2 min-h-24 w-full resize-y"
            rows="3"
            maxlength="300"
            placeholder="Optional note for this browser session"
            data-review-note
          ></textarea>
          <div class="mt-4 flex flex-wrap gap-3">
            <button type="button" class="zag-btn" data-set-status="reviewed">
              Mark reviewed
            </button>
            <button type="button" class="zag-btn" data-set-status="escalated">
              Escalate
            </button>
          </div>
        </section>
        <p
          class="self-start pt-6 text-sm zag-muted lg:border-l lg:border-t-0 lg:pl-8 lg:pt-0"
          data-empty-state
          hidden
        >
          No records match this filter.
        </p>
      </div>
      <p
        class="mt-6 min-h-6 text-sm zag-muted"
        role="status"
        aria-live="polite"
        data-workbench-message
      >
        Select a record to review. Changes remain in this browser session.
      </p>
    </div>

    <section
      id="method"
      class="mt-12 max-w-prose"
      aria-labelledby="method-heading"
    >
      <h2 id="method-heading" class="mb-4 page-title text-xl sm:text-2xl">
        Design boundary
      </h2>
      <p>
        The prototype demonstrates a review flow: calculate plan-to-actual
        variance, expose the fixture context and source identifiers, then let a
        person mark a record reviewed or escalate it. The values, teams,
        periods, and source IDs are invented for this demo.
      </p>
      <p>
        It does not call an LLM, connect to finance systems, persist decisions,
        establish accounting policy, or measure a user's time saved. A real
        deployment would need permission-aware source integration, materiality
        rules agreed with finance, audit persistence, security review, failure
        handling, and an evaluation set reviewed by domain experts.
      </p>
    </section>
  </Section>
</Layout>

<script>
  import {
    calculateVariance,
    sampleVarianceRecords,
    updateReviewStatus,
  } from "@/lib/variance-workbench";

  const workbench = document.querySelector("[data-variance-workbench]");
  const recordList = workbench?.querySelector("[data-record-list]");
  const detail = workbench?.querySelector("[data-record-detail]");
  const emptyState = workbench?.querySelector("[data-empty-state]");
  const noteInput = workbench?.querySelector("[data-review-note]");
  const message = workbench?.querySelector("[data-workbench-message]");
  const money = new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    maximumFractionDigits: 0,
  });

  if (
    workbench instanceof HTMLElement &&
    recordList instanceof HTMLUListElement &&
    detail instanceof HTMLElement &&
    emptyState instanceof HTMLElement &&
    noteInput instanceof HTMLTextAreaElement &&
    message instanceof HTMLElement
  ) {
    const workbenchRoot = workbench;
    const queue = recordList;
    const detailView = detail;
    const emptyStateMessage = emptyState;
    const reviewerNote = noteInput;
    const statusMessage = message;
    let records = sampleVarianceRecords.map((record) => ({
      ...record,
      sources: [...record.sources],
    }));
    let selectedId = records[0].id;
    let activeFilter = "all";

    const statusLabels: Record<
      (typeof sampleVarianceRecords)[number]["status"],
      string
    > = {
      "needs-review": "Needs review",
      reviewed: "Reviewed",
      escalated: "Escalated",
    };

    function requiredChild<T extends Element>(
      root: HTMLElement,
      selector: string,
    ): T {
      const element = root.querySelector<T>(selector);
      if (!element) throw new Error(`Missing workbench element: ${selector}`);
      return element;
    }

    function signedMoney(amount: number) {
      const prefix = amount > 0 ? "+" : amount < 0 ? "−" : "";
      return `${prefix}${money.format(Math.abs(amount))}`;
    }

    function visibleRecords() {
      return activeFilter === "all"
        ? records
        : records.filter((record) => record.status === activeFilter);
    }

    function renderQueue() {
      queue.replaceChildren();
      for (const record of visibleRecords()) {
        const variance = calculateVariance(record.plan, record.actual);
        const item = document.createElement("li");
        const button = document.createElement("button");
        const wrapper = document.createElement("span");
        const identity = document.createElement("span");
        const title = document.createElement("span");
        const subtitle = document.createElement("span");
        const amount = document.createElement("span");
        const status = document.createElement("span");

        button.type = "button";
        button.className = "w-full py-4 text-left";
        button.dataset.recordId = record.id;
        button.setAttribute("aria-pressed", String(record.id === selectedId));
        wrapper.className = "flex items-start justify-between gap-3";
        identity.className = "min-w-0";
        title.className = "block font-medium";
        title.textContent = record.account;
        subtitle.className = "mt-1 block text-xs zag-muted";
        subtitle.textContent = `${record.team} · ${record.id}`;
        amount.className = "shrink-0 text-right font-mono text-sm tabular-nums";
        amount.textContent = signedMoney(variance.amount);
        status.className = "mt-1 block text-xs zag-muted";
        status.textContent = statusLabels[record.status];
        identity.append(title, subtitle);
        amount.append(status);
        wrapper.append(identity, amount);
        button.append(wrapper);
        item.append(button);
        queue.append(item);
      }
    }

    function renderDetail() {
      const record = records.find((item) => item.id === selectedId);
      if (!record) return;
      const variance = calculateVariance(record.plan, record.actual);

      requiredChild<HTMLElement>(detailView, "[data-detail-id]").textContent =
        record.id;
      requiredChild<HTMLElement>(
        detailView,
        "[data-detail-account]",
      ).textContent = record.account;
      requiredChild<HTMLElement>(detailView, "[data-detail-team]").textContent =
        `${record.team} · ${record.period}`;
      requiredChild<HTMLElement>(
        detailView,
        "[data-detail-status]",
      ).textContent = statusLabels[record.status];
      requiredChild<HTMLElement>(detailView, "[data-detail-plan]").textContent =
        money.format(record.plan);
      requiredChild<HTMLElement>(
        detailView,
        "[data-detail-actual]",
      ).textContent = money.format(record.actual);
      requiredChild<HTMLElement>(
        detailView,
        "[data-detail-amount]",
      ).textContent = signedMoney(variance.amount);
      requiredChild<HTMLElement>(
        detailView,
        "[data-detail-percentage]",
      ).textContent =
        variance.percentage === null
          ? "Not defined"
          : `${variance.percentage > 0 ? "+" : ""}${variance.percentage}%`;
      requiredChild<HTMLElement>(
        detailView,
        "[data-detail-driver]",
      ).textContent = record.driver;

      const sourceList = requiredChild<HTMLUListElement>(
        detailView,
        "[data-detail-sources]",
      );
      sourceList.replaceChildren();
      for (const source of record.sources) {
        const item = document.createElement("li");
        item.className = "border zag-border px-2 py-1 font-mono text-xs";
        item.textContent = source;
        sourceList.append(item);
      }

      reviewerNote.value = record.reviewNote ?? "";
      for (const button of detailView.querySelectorAll("[data-set-status]")) {
        if (!(button instanceof HTMLButtonElement)) continue;
        button.disabled = button.dataset.setStatus === record.status;
      }
    }

    function renderCounts() {
      const count = (
        status: (typeof sampleVarianceRecords)[number]["status"],
      ) => records.filter((record) => record.status === status).length;
      requiredChild<HTMLElement>(
        workbenchRoot,
        "[data-count-total]",
      ).textContent = String(records.length);
      requiredChild<HTMLElement>(
        workbenchRoot,
        "[data-count-needs-review]",
      ).textContent = String(count("needs-review"));
      requiredChild<HTMLElement>(
        workbenchRoot,
        "[data-count-reviewed]",
      ).textContent = String(count("reviewed"));
      const netVariance = records.reduce(
        (sum, record) => sum + record.actual - record.plan,
        0,
      );
      requiredChild<HTMLElement>(
        workbenchRoot,
        "[data-net-variance]",
      ).textContent = signedMoney(netVariance);
    }

    function render() {
      const available = visibleRecords();
      if (available.length === 0) {
        selectedId = "";
        detailView.hidden = true;
        emptyStateMessage.hidden = false;
        renderQueue();
        renderCounts();
        statusMessage.textContent = "No records match this filter.";
        return;
      }
      detailView.hidden = false;
      emptyStateMessage.hidden = true;
      if (!available.some((record) => record.id === selectedId)) {
        selectedId = available[0]?.id ?? "";
      }
      renderQueue();
      renderDetail();
      renderCounts();
    }

    workbenchRoot.addEventListener("click", (event) => {
      const target = event.target;
      if (!(target instanceof Element)) return;

      const recordButton = target.closest("[data-record-id]");
      if (recordButton instanceof HTMLButtonElement) {
        const recordId = recordButton.dataset.recordId;
        if (!recordId) return;
        selectedId = recordId;
        render();
        return;
      }

      const filterButton = target.closest("[data-filter]");
      if (filterButton instanceof HTMLButtonElement) {
        activeFilter = filterButton.dataset.filter ?? "all";
        for (const button of workbenchRoot.querySelectorAll("[data-filter]")) {
          button.setAttribute("aria-pressed", String(button === filterButton));
        }
        render();
        return;
      }

      const statusButton = target.closest("[data-set-status]");
      if (statusButton instanceof HTMLButtonElement) {
        const nextStatus = statusButton.dataset.setStatus;
        if (nextStatus !== "reviewed" && nextStatus !== "escalated") return;
        records = updateReviewStatus(
          records,
          selectedId,
          nextStatus,
          reviewerNote.value.trim() || undefined,
        );
        statusMessage.textContent = `${selectedId} marked ${statusLabels[nextStatus].toLowerCase()}. Nothing was written to an external system.`;
        render();
        return;
      }

      if (target.closest("[data-reset-workbench]")) {
        records = sampleVarianceRecords.map((record) => ({
          ...record,
          sources: [...record.sources],
        }));
        selectedId = records[0].id;
        activeFilter = "all";
        for (const button of workbenchRoot.querySelectorAll("[data-filter]")) {
          button.setAttribute(
            "aria-pressed",
            String(button.getAttribute("data-filter") === "all"),
          );
        }
        statusMessage.textContent = "Sample reset. No decisions were saved.";
        render();
      }
    });

    render();
  }
</script>