Use this when a Temporal TypeScript Workflow needs formula-backed decisions, but the calculation should run through an Activity instead of Workflow replay code.
Temporal’s TypeScript guide separates deterministic Workflow code from Activities. Activities are the right boundary for WorkPaper state, file I/O, Excel/XLSX import, object storage, and formula recalculation. This example keeps that boundary: Temporal owns durable orchestration, retries, workflow history, and replay; Bilig owns formula workbook state, recalculation, JSON serialization, restore, and readback proof.
Official Temporal references:
The runnable source lives in:
examples/temporal-workpaper-activity
It contains:
src/workflows.ts for the deterministic Workflow boundarysrc/activities.ts for the WorkPaper formula Activitysrc/smoke.ts for a local Activity smoke through MockActivityEnvironmentscripts/check-temporal-boundary.ts for import-boundary checksRun the proof locally:
cd examples/temporal-workpaper-activity
pnpm install --ignore-workspace --lockfile=false
pnpm run check
pnpm run smoke
The smoke edits Inputs!B2, recalculates quote formulas, serializes the
WorkPaper document, restores it, verifies restored calculated values match,
prints JSON, and writes .tmp/workpaper-proof.json.
The checked-in Workflow never imports Bilig:
import { proxyActivities } from '@temporalio/workflow'
import type { TemporalWorkPaperActivities } from './types'
const { calculateWorkPaperQuoteActivity } = proxyActivities<TemporalWorkPaperActivities>({
startToCloseTimeout: '30 seconds',
retry: {
maximumAttempts: 3,
},
})
export async function quoteApprovalWorkflow(input) {
return await calculateWorkPaperQuoteActivity(input)
}
The Activity imports @bilig/workpaper, writes one input, recalculates formulas,
exports WorkPaper JSON, restores the document, and returns a compact
serializable proof.
The proof contains:
{
"patch": {
"subtotal": 2250,
"discount_amount": 225,
"taxable_amount": 2025,
"tax_amount": 162,
"total": 2187,
"margin_amount": 1089
},
"proof": {
"editedCell": "Inputs!B2",
"before": {
"total": 1458
},
"after": {
"total": 2187
},
"afterRestore": {
"total": 2187
},
"verified": true
},
"temporalBoundary": {
"workflowImportsWorkPaper": false,
"activityOwnsWorkPaper": true,
"payloadShape": "serializable-patch-and-proof"
}
}
Keep the full WorkPaper document in an artifact path, database, or object store when it grows beyond a compact Activity result.
Before using this pattern for customer-critical Workflows:
@bilig/workpaper, XLSX import/export, file I/O, network calls, and
object-store writes in Activities.WorkflowReplayer against captured histories when Workflow code changes.Use it for quote approvals, pricing rules, payout checks, import validation, order-review gates, and durable workflows where spreadsheet formulas are the most reviewable representation of business logic.
Do not use it to pretend Bilig is desktop Excel. Use Temporal for orchestration and replay, Activities for WorkPaper formula work, and external oracles for Excel-specific behavior.
If this is shared in a Temporal issue, forum thread, or sample request, lead with the concrete boundary it solves:
Temporal owns durable orchestration and replay. Bilig owns formula workbook recalculation inside an Activity and returns both calculated values and readback proof.
Do not post it as a generic spreadsheet-engine pitch. Link the runnable example and smoke output, then ask whether a formula-backed Activity boundary would be useful to TypeScript SDK users.