bilig

Building A Revenue Model With Headless WorkPaper

Status: public adoption article for @bilig/headless

Most spreadsheet automation demos stop at “generate a formula.” A service or agent needs more than that. It needs to load workbook state, edit inputs, recalculate formulas, verify outputs, and persist the document for the next turn.

This article uses the runnable examples/headless-workpaper/revenue-scenarios.mjs example to show that loop with @bilig/headless.

The Model

The example builds three sheets:

The key point is that the formulas stay in the workbook:

const workbook = WorkPaper.buildFromSheets({
  Pipeline: [
    ['Segment', 'Leads', 'Conversion Rate', 'Customers', 'ARPA', 'Gross MRR', 'Churn Rate', 'Net MRR'],
    ['Enterprise', 80, 0.18, '=B2*C2', 4200, '=D2*E2', 0.05, '=F2*(1-G2)'],
    ['Mid-Market', 220, 0.22, '=B3*C3', 1100, '=D3*E3', 0.08, '=F3*(1-G3)'],
    ['SMB', 900, 0.09, '=B4*C4', 180, '=D4*E4', 0.12, '=F4*(1-G4)'],
  ],
  Summary: [
    ['Metric', 'Value'],
    ['Total net MRR', '=SUM(Pipeline!H2:H4)'],
    ['Annual run rate', '=B2*12'],
    ['Enterprise net MRR', '=SUMIF(Pipeline!A2:A4,"Enterprise",Pipeline!H2:H4)'],
    ['Expansion target', '=B3*1.18'],
  ],
})

That means the model can be changed by code without turning the spreadsheet into a pile of copied numbers.

The Agent-Style Edit

The example applies a focused planning change in a single batch:

workbook.batch(() => {
  workbook.setCellContents({ sheet: pipelineSheet, row: 1, col: 1 }, 92)
  workbook.setCellContents({ sheet: pipelineSheet, row: 2, col: 2 }, 0.26)
})

The first edit changes enterprise leads from 80 to 92. The second changes mid-market conversion from 0.22 to 0.26.

After that, formulas recalculate through the model:

Those are not hand-copied outputs. The script reads them back from cells and fails if the values drift.

The Persistence Loop

The example also serializes and restores the workbook before final readback:

const serialized = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }))
const restored = createWorkPaperFromDocument(parseWorkPaperDocument(serialized))

That is the operational pattern for agents and services:

  1. load workbook state
  2. apply a narrow edit
  3. recalculate
  4. persist the workbook document
  5. restore it later and verify the same formulas

Run It

From the repository:

cd examples/headless-workpaper
npm install
npm run scenarios

The expected output includes both the pre-edit and post-edit model:

{
  "beforeEdit": {
    "totalNetMrr": 119267.2,
    "annualRunRate": 1431206.4,
    "enterpriseNetMrr": 57456,
    "expansionTarget": 1688823.55
  },
  "afterEdit": {
    "totalNetMrr": 136791.2,
    "annualRunRate": 1641494.4,
    "enterpriseNetMrr": 66074.4,
    "expansionTarget": 1936963.39
  }
}

The repository smoke test also runs this scenario against packed local runtime packages:

pnpm workpaper:smoke:external

Why This Matters

Spreadsheet-backed products often need the spreadsheet model without the browser grid:

@bilig/headless is meant for that boundary. It does not claim to be a complete Excel clone; it gives code a typed workbook surface with formulas, structural edits, persistence, and benchmark evidence in the same TypeScript package.