DEV Community

SybilGambleyyu
SybilGambleyyu

Posted on • Originally published at sybilgambleyyu.github.io

A benchmark for reviewing spreadsheet changes, not just editing them

A benchmark for reviewing spreadsheet changes, not just editing them

Spreadsheet tools are increasingly good at writing a formula or producing a
finished workbook. The harder operational question comes afterward: a workbook
changed—what changed, which other formulas can it reach, should it block a
review, and what could the tool not determine?

Workbook Change Assurance Benchmark (WCAB)
is a small, open way to make those claims testable. Version 0.36.0 contains
53 deterministic scenarios: 52 baseline/candidate workbook pairs and one
directory portfolio. Together they declare 55 observable facts, a benchmark
review disposition, and—where appropriate—a static dependency-impact lower
bound. The workbook files are generated from source, not copied from a
financial model, email archive, or other sensitive corpus.

Version 0.36.0 includes a deterministic, one-row-per-case manifest.jsonl
catalogue. It carries the truth contract alongside exact relative paths, byte
counts, and SHA-256 digests for every baseline and candidate workbook, so an
evaluator can identify precisely which fixtures it consumed. A pair is
explicitly either a matching .xlsx pair or a matching .xlsm pair. The same
release is mirrored as a Hugging Face dataset.
It retains the schema-version-3 truth contract, including an Excel Table
scope-expansion case: =SUM(SalesLedger[Amount]) stays unchanged while the
stored Table range grows from A1:D4 to A1:D5. Microsoft documents
that structured references adjust when a Table gains or loses data. Schema v3
also covers a direct-to-INDIRECT change. Version 0.5.0 adds scoreable
requirements to disclose both that boundary and an unchanged INDIRECT or
OFFSET formula whose selector changes.

Version 0.34.0 also adds two stored formula-definition boundaries. A
workbook-scoped ScenarioValue name moves from
=LAMBDA(rate,amount,rate*amount) to
=LAMBDA(rate,amount,rate*(amount+10)) while its inputs, calling
Model!B2 formula, dashboard consumer, and every package member except
xl/workbook.xml stay fixed. A local ScenarioLedger Table instead keeps its
binding, headers, Ledger!C2:C4 formulas, and structured-reference dashboard
formula fixed while its third-column raw calculatedColumnFormula moves from
A2*B2 to A2*(B2+1) in xl/tables/table1.xml. These cases record stored
formula programs only: WCAB does not evaluate a LAMBDA, fill a Table column,
calculate a structured reference, infer a result, or claim client behavior.

Version 0.35.0 adds a Data Model relationship boundary that ordinary cell and
formula diffs can miss. Microsoft's PowerPivot Model
overview

explains that relationships filter data across model tables, while the Office
Open XML definitions for CT_DataModel
and CT_ModelRelationship
define the stored table and key declarations. The compact pair keeps two local
Excel Tables, SalesModel and CalendarModel, their worksheet cells, the
powerPivotData workbook binding, content type, calculation properties, and a
fixed opaque xl/model/item.data payload unchanged. Only the raw Office 2013
x15:modelRelationship in xl/workbook.xml moves from
SalesModel.CalendarKey → CalendarModel.DateKey to
SalesModel.CalendarKey → CalendarModel.FiscalDateKey. WCAB verifies that
stored declaration and the payload digest only: it does not deserialize a Data
Model, evaluate DAX, refresh, calculate, render a report, infer model-to-cell
impact, or claim Excel-client behavior.

Version 0.36.0 adds a stored automatic-macro binding boundary that ordinary
formula and macro-sheet-content diffs can miss. Microsoft's Excel 4.0 macro
guidance

says Excel continues to support XLM macros, and the
XlRunAutoMacro reference
names Auto_Open, Auto_Close, Auto_Activate, and Auto_Deactivate as
automatic macro events. The new fixture is a real macro-enabled .xlsm pair:
one workbook-scoped _xlnm.Auto_Open name moves from
'Macro Automation'!$A$1 to 'Macro Automation'!$A$2. Its one very-hidden
XLM macro-sheet part remains byte-identical and contains only two static
HALT() formula cells. The macro-sheet relationship, content types, ordinary
Inputs!B2 → Model!B2 → Dashboard!B4 formula context, calculation properties,
and every package member except xl/workbook.xml remain fixed. WCAB records
the stored dispatch declaration only: it never opens Excel, enables or executes
XLM code, parses or emulates macro instructions, resolves a dynamic name,
inspects macro-security or trust settings, or claims a dispatch result.

Those selector cases are the important addition: formula text remains stable,
but an input changes either the address text consumed by INDIRECT or the
column displacement consumed by OFFSET. The workbook can therefore select a
different effective target even when a formula-text diff says nothing changed.

Version 0.6.0 adds a different unchanged-cell risk: an external-data
connection starts refreshing when the workbook opens. Excel documents a
Refresh data when opening the file
setting for connections. The paired fixture leaves every worksheet cell and
formula unchanged and changes only the relationship-backed connection's
refreshOnLoad attribute from false to true. Its endpoint is a non-routable
example.invalid URL; WCAB never opens it, requests credentials, refreshes
data, or claims a calculated result.

Version 0.7.0 adds another unchanged-formula risk: a legacy Ctrl+Shift+Enter
(CSE) array changes into a dynamic array. Excel distinguishes
fixed CSE output ranges from dynamic arrays that can resize. The paired
fixture holds =LEN(Inputs!A1:A3) and its currently stored B1:B3 range
constant, then adds the raw OOXML metadata binding for Model!B1.
WCAB validates that stored mode change but never calculates the formula,
predicts a future spill extent, finds blockers, or claims client compatibility.

Version 0.8.0 adds a separate unchanged-formula risk: an external-workbook
link can switch from never updating to always updating at workbook open.
Excel's workbook-link guidance
describes those startup choices, while the Open XML workbook-properties
reference

identifies updateLinks as the stored open-time control. The new pair preserves
='[WCABSource.xlsx]Inputs'!$B$2 and its local downstream formula but changes
only workbookPr/@updateLinks from never to always. The source workbook is
synthetic and absent; WCAB never opens it, tests trust or authentication,
retrieves a value, or claims recalculation succeeded.

Version 0.9.0 adds a direct circular formula whose stored iteration control is
enabled without changing the formula or its local downstream consumer.
Excel's circular-reference guidance
explains that iterative calculation can intentionally allow circular references,
and the Open XML calculation-properties reference
defines iterate, iterateCount, and iterateDelta. The new pair preserves
=(B2+Inputs!$B$2)/2 and changes only calcPr/@iterate from false to true;
both workbooks store 100 iterations and a 0.001 delta. WCAB reads those stored
controls but never calculates the circular model, asserts convergence, predicts
an iteration count, or reports a terminal value.

Version 0.10.0 adds a different unchanged-cell risk: precision as displayed
can be enabled while the stored input, number format, and formulas still match.
Excel's calculation guidance
says that calculating with displayed values permanently changes stored values,
and its rounding-precision guidance
warns of cumulative effects. The Open XML calculation-properties
reference

defines the corresponding fullPrecision control. The pair preserves the raw
Inputs!B2=10.005 value, its 0.00 format, =Inputs!$B$2*2, and the local
consumer, then changes only calcPr/@fullPrecision from true to false. WCAB
reads package metadata and stored cells only: it never opens or saves Excel,
calculates a formula, asserts that a value rounded, predicts a result, or claims
a particular client applies the setting.

Version 0.11.0 adds a saved-result boundary that formula text alone cannot
cover. Microsoft's SpreadsheetML formula guidance
stores a formula expression in <f> and the result saved from its last
calculation in neighboring <v>. The pair retains Inputs!B2=10,
Model!B2's =Inputs!$B$2*2 expression, calculation properties, and the
local Dashboard!B4 consumer, while changing only the raw numeric <v> from
20 to 25. WCAB does not calculate the formula, say either saved result is
current, stale, tampered, or correct, or claim what a client will display after
opening.

Version 0.12.0 adds a workbook-wide serial-date-system control change with no
cell edit. Excel documents 1900 and 1904 date systems with a 1,462-day
difference for the same stored serial in its date-system guidance.
The pair preserves raw Inputs!B2=45292, its yyyy-mm-dd format, and local
formulas while only workbookPr/@date1904 changes from false to true;
dateCompatibility=true remains explicit. WCAB reads raw OOXML, styles, and
formula text only. It does not calculate a formula, convert a serial, predict a
displayed date, or claim Excel-client behavior.

Version 0.13.0 adds an active worksheet AutoFilter criterion change without a
cell edit. Excel's filter guidance
explains that filters show matching data and hide the rest, and that the
filtered subset can be copied, charted, or printed. Its
SUBTOTAL documentation
states that filter-excluded rows are always excluded. The pair changes the
sole column-0 list value in Report!A1:B5 from North to South, while
Report!D2=SUBTOTAL(109,B2:B5) and Dashboard!B4=Report!$D$2 remain fixed.
The raw validator proves that stored transition, stable formulas and direct
dependency edge, and the report-worksheet-only package difference. It does not
apply a filter, calculate the subtotal, infer visible rows, or claim what an
Excel client displays, copies, charts, or prints.

Version 0.14.0 adds a relationship-backed PivotTable cache refresh-on-open
control without a cell edit. Excel's PivotTable refresh guidance
includes refreshing data when a workbook opens, and the Open XML
PivotCacheDefinition reference
defines refreshOnLoad. The pair binds a local Source!A1:B5 cache through a
PivotTable at Report!A1:B2 and a direct Dashboard!B4=Report!$B$2 consumer.
Every source cell, cache record, stored report cell, and formula stays fixed;
only pivotCacheDefinition/@refreshOnLoad moves from false to true. WCAB reads
the raw relationship-backed package. It does not open Excel, refresh a cache,
calculate or render a PivotTable, infer a result, or claim that a client honors
the request.

Version 0.15.0 adds a DrawingML chart-series source-reference case without a
worksheet-cell edit. Excel's series guidance
allows a series to use a different source range, while the Open XML
NumberReference reference
models that binding. The pair keeps Dashboard!D2, the title 'Source'!B1,
the category range 'Source'!$A$2:$A$4, and every worksheet cell fixed, while
its raw numeric-series source moves from 'Source'!$B$2:$B$4 to
'Source'!$C$2:$C$4. The validator follows the worksheet-to-drawing-to-chart
relationship chain and proves that only xl/charts/chart1.xml changes. It
does not calculate, refresh, render, infer a visible difference, or claim
client behavior.

Version 0.16.0 adds a relationship-backed PivotTable value-field aggregation
case without a cell edit. Excel's PivotTable layout guidance
describes changing a field's Value settings, while the Open XML
DataField.Subtotal reference
identifies the stored data-consolidate function. The pair retains its local
Source!A1:B5 cache binding, all cache records, Report!A1:B2 location,
stored Report!B2 display cell, and Dashboard!B4=Report!$B$2 formula; only
the one dataFields/dataField/@subtotal changes from sum to average.
The validator proves the cache/PivotTable relationship graph and that only the
PivotTable definition part changes. It does not refresh, calculate, or render
a PivotTable, infer a changed display value, or claim client behavior.

Version 0.17.0 adds a relationship-backed PivotTable Slicer-selection case
without a cell edit. Excel's PivotTable filtering guidance
describes Slicers as controls that filter PivotTables and convey filtering
state. The Office Open XML Slicer Cache Part specification
stores cache-item indices with x and selected items with s=1. The pair
retains its local Source!A1:B5 cache, Report!A1:B2 PivotTable, stored
Report!B2 display cell, and Dashboard!B4=Report!$B$2 formula; only the
selected Region item moves from index 0 (North) to index 1 (South). The
validator follows the workbook-to-Slicer-cache-to-PivotCache/PivotTable graph
and proves that only xl/slicerCaches/slicerCache1.xml changes. This fixture
has no visual Slicer or drawing: it does not apply a filter, refresh, calculate,
or render a PivotTable, infer a changed result, or claim client behavior.

Version 0.18.0 adds a connection-only Power Query M filter case without a
worksheet edit. Power Query's overview
explains that transformations are stored as M, and its query-management
guidance

allows a query to be connection-only. The pair keeps a local
Source!A1:B5 SourceData Table, every worksheet cell, metadata, permission
control, and calculation property fixed while its stored
Table.SelectRows Region literal moves from North to South inside
customXml/item1.xml. The validator follows the package-root custom-XML
relationship and a bounded generated Data Mashup envelope. It does not execute
M, apply the filter, refresh a query, materialize output, calculate a workbook,
infer returned rows, or claim client behavior.

Version 0.19.0 adds a Scenario Manager alternate-input case without a worksheet
edit. Excel’s Scenario Manager guidance
describes scenarios as saved sets of values for changing cells. The pair keeps
Inputs!B2=0.1, Inputs!B3=125, Inputs!D2=B2*B3, and
Dashboard!B4=Inputs!$D$2 fixed. In the selected locked WCAB downside
scenario, only the raw stored Inputs!B2 value moves from 0.08 to 0.16.
The scenario remains stored rather than shown or applied. The validator proves
the exact scenario metadata and that only xl/worksheets/sheet1.xml differs;
it does not show or apply a scenario, calculate a formula, create a scenario
summary, infer a result, or claim Excel-client behavior.

Version 0.20.0 adds a one-variable What-If Data Table input-reference case
without a formula edit. Excel's Data Table guidance
distinguishes one- and two-variable tables and their row or column input cells.
The Open XML CellFormula reference
defines the dataTable master formula with its output range, input references,
and orientation controls. The pair keeps the column-oriented
Sensitivity!D3 master's D3:D5 output range, recalculation request, input
grid, ordinary formulas, calculation properties, and saved table results fixed
while only raw f/@r1 moves from B2 to B3. The validator proves that the
Sensitivity worksheet is the sole changed package member and that both possible
input cells retain ordinary static paths to the model and dashboard. It does
not substitute inputs, calculate a table or workbook, infer output values,
resolve a circular dependency, or claim Excel-client behavior.

Version 0.21.0 adds a list data-validation source case without a worksheet
cell edit. Excel's data-validation guidance
describes list validation, and the Open XML Formula1 reference
stores the rule's first formula. The pair keeps the Inputs!B2 target, its
blank/dropdown/error/prompt controls, both Lists source columns, the current
Draft input, and the Model!B2=Inputs!$B$2 / Dashboard!B4=Model!$B$2
formulas fixed. Only raw formula1 moves from =Lists!$A$2:$A$4 to
=Lists!$B$2:$B$4. The validator proves that the Inputs worksheet is the sole
changed package member and treats the direct formula path as a lower bound if a
user later enters a value. It does not evaluate either list, decide whether a
future input is valid, accept or reject an entry, calculate the workbook, or
claim Excel-client behavior.

Version 0.22.0 adds a stored conditional-formatting threshold case without a
cell edit. Microsoft's Open XML guidance
describes conditional-formatting rules stored alongside a worksheet, and its
formula specification
separates the rule expression from a rendered worksheet outcome. The pair holds
one Operations!B2:B4 cellIs rule's target, priority 1,
greaterThan operator, values 10, 75, and 120, and solid
FFFFC7CE differential fill fixed. Only the raw rule formula moves from
100 to 50. The validator proves the Operations worksheet is the sole
changed package member and that its XML is otherwise identical after erasing
that selected formula text. It does not evaluate a condition, calculate a
workbook, decide which values are highlighted, render a worksheet, or claim
Excel-client behavior.

Version 0.23.0 adds a stored custom number-format case without a cell edit.
Excel's custom-number-format guidance
documents ;;; as a way to hide worksheet values, while its
percentage-format guidance
describes changing the display of a stored number. The pair holds
Operations!B2's raw 0.125 value, custom format ID 164, cell-style
assignment, and Operations!B3=B2 formula fixed. Only the custom format code
moves from 0.0%;[Red](0.0%);- to ;;;. The validator proves that only
xl/styles.xml changes and that it is otherwise identical after erasing the
selected format code. It does not render a cell, apply locale or column-width
rules, infer a displayed result, calculate a workbook, or claim Excel-client
behavior.

Version 0.24.0 adds a stored error-checking suppression without a formula or
cell edit. Microsoft's formula-error guidance
explains that an ignored error does not appear in later error checks until it is
reset, and documents the formula-range-omission rule. The pair keeps
Operations!B2=10, B3=20, B4=30, B5=SUM(B2:B3), and C5=B5 fixed.
Only one standard ignoredErrors/ignoredError declaration with sqref="B5"
and formulaRange="1" is added. The validator proves that only
xl/worksheets/sheet1.xml changes and that it is otherwise identical after
removing that declaration. It does not determine whether Excel would show a
warning, evaluate a formula, decide whether suppression is justified, render
an indicator, change application-level error checking, calculate a workbook,
or claim Excel-client behavior.

Version 0.25.0 adds a stored workbook-structure protection change without a
cell edit. Microsoft's workbook-protection guidance
distinguishes structure protection from file and worksheet protection and says
that it restricts adding, moving, deleting, hiding, unhiding, and renaming
sheets. Its Open XML specification
records the stored workbook control. The pair keeps the hidden
ReviewControls sheet and Inputs!D2=B2*C2 fixed. Only
workbookProtection/@lockStructure moves from 1 to 0; the validator proves
that xl/workbook.xml is the only changed package member and otherwise equal
after removing that attribute. It does not test a password, encryption,
authentication, authorization, hidden-sheet exposure, or client
sheet-operation behavior.

Version 0.26.0 adds a relationship-backed saved Excel Named Sheet View case
without a cell edit. Microsoft documents Sheet Views
as saved customized filter and sort views, and the MS-XLSX Named Sheet Views
specification

defines the stored collection as sort/filter settings associated with a
worksheet AutoFilter. The pair keeps a no-criterion Report!A1:B5 base
AutoFilter, rows, Report!D2=SUBTOTAL(109,B2:B5), and
Dashboard!B4=Report!$D$2 fixed. A worksheet relationship binds that base
filter to one Named Sheet View part; only its saved column-0 list value moves
from North to South. The validator follows the relationship and filter ID
and proves that xl/namedSheetViews/namedSheetView1.xml is the sole changed
package member. It does not activate, render, or apply a view; calculate a
subtotal; infer visible rows; or claim a display or print outcome.

Version 0.27.0 adds an Excel XML Map table-column binding retargeting without
a cell edit. Microsoft's XML overview
describes XML Maps as bindings between schema elements and worksheet cells or
XML tables for importing and exporting XML data, while its
XmlMap API
exposes those import and export operations. The pair keeps a synthetic local
MapInfo/XSD declaration, file-binding metadata, an Export!E2 single-cell
mapping, table values, Export!D2=SUM(InvoiceLines[Net amount]), and
Dashboard!B4=Export!$D$2 fixed. Only the InvoiceLines table's
Net amount xmlColumnPr/@xpath moves from NetAmount to TaxAmount.
The validator follows local workbook and worksheet relationships and proves
xl/tables/table1.xml is the sole changed package member. It does not access
a file, validate a schema, import or export XML, materialize data, calculate a
result, or claim client behavior.

Version 0.28.0 adds an Office Web Add-in task-pane auto-show request without a
cell edit. Microsoft's workbook auto-open guidance
documents the package association and makes clear that the add-in must already
be installed, sideloaded, or deployed before an application can honor it. The
MS-OWEMXML specification
defines Office.AutoShowTaskpaneWithDocument as an extension property. The
pair keeps a synthetic local FileSystem reference, a hidden locked task pane,
ordinary cells, calculation properties, and the
Inputs!B2 → Model!B2 → Dashboard!B4 formula context fixed. Only that
property moves from false to true, and
xl/webextensions/webextension1.xml is the sole changed package member.
There is no manifest payload or external relationship. WCAB does not install,
load, execute, or fetch an add-in or manifest, and does not claim that a task
pane opens or that an add-in accesses a workbook cell.

Version 0.29.0 adds a worksheet embedded-OLE auto-load request without a cell
edit. Microsoft's OLEObject.AutoLoad reference
describes the stored request, and the Open XML property reference
identifies the SpreadsheetML attribute. The pair retains one direct internal
OLE-object relationship, a matching content type, opaque fixed ASCII bytes, a
synthetic unregistered ProgID, ordinary cells, and the
Inputs!B2 → Model!B2 → Dashboard!B4 formula context. Only
oleObject/@autoLoad moves from false to true, and
xl/worksheets/sheet1.xml is the sole changed package member. There is no
linked target, ActiveX control, presentation, macro, or external relationship.
WCAB does not deserialize, open, render, execute, register, or invoke an object
server, and does not claim that an object loads successfully.

Version 0.30.0 adds a distinct QueryTable refresh-on-open request without a
cell edit. Microsoft’s QueryTable.RefreshOnFileOpen reference
describes a table-level automatic-refresh control, while the Open XML
QueryTable reference

shows the stored refreshOnLoad field alongside its connection ID. The pair
retains one ImportedData worksheet-to-QueryTable relationship, one internal
workbook-to-connections relationship, the reserved non-routable
example.invalid endpoint, connection-level refreshOnLoad=false, saved
ImportedData!B2=100, and the
ImportedData!B2 → Summary!B2 → Dashboard!B4 formula context. Only
xl/queryTables/queryTable1.xml changes as queryTable/@refreshOnLoad moves
from false to true. WCAB does not open a connection, fetch a URL, refresh a
query, materialize rows, calculate a workbook, or claim that any client
refreshes successfully.

Version 0.31.0 adds a relationship-backed worksheet cell hyperlink target case
without changing a visible cell value. Microsoft's
Hyperlink.Address reference
defines the target document address, while the Open XML Hyperlink
reference

identifies worksheet x:hyperlink/@r:id as the relationship binding that
expresses that target. The pair retains Inputs!B2's visible Open vendor
portal
text, its worksheet declaration, relationship ID/type/mode, calculation
properties, and Inputs!B2 → Summary!B2 → Dashboard!B4 formula context. Only
the one external relationship Target in
xl/worksheets/_rels/sheet1.xml.rels moves between reserved example.invalid
URLs. WCAB reads local OOXML only: it does not resolve, open, fetch, visit,
execute, calculate, or claim that a client follows either target.

Version 0.32.0 adds a relationship-backed external-workbook source-retarget
case without changing formula text. Excel's workbook-link guidance
documents Change source, while the Open XML ExternalBook reference
describes an external workbook supplying data through a relationship to its
supporting book path. The pair retains
LinkedModel!B2 with ='[WCABSource.xlsx]Inputs'!$B$2, its direct
Dashboard!B4=LinkedModel!$B$2 consumer, calculation properties, workbook
external-reference binding, externalLink/externalBook declaration,
source-sheet name, relationship IDs/types, and content type. Only
xl/externalLinks/_rels/externalLink1.xml.rels changes: one external
Relationship/@Target moves between reserved example.invalid sources. WCAB
reads local OOXML only; it does not resolve, open, fetch, authenticate to,
trust, refresh, calculate, or claim that a client updates a link or returns a
value.

Version 0.33.0 adds two smaller governance boundaries that ordinary cell diffs
can miss. Microsoft’s workbook-link guidance
identifies defined names as a location for workbook links, and the Open XML
DefinedNames reference
describes the stored workbook-level collection. The first new pair retains
Model!B2=ScenarioRate*2 and its direct Dashboard!B4=Model!$B$2 consumer,
but its local ScenarioRate definition moves from
'[WCABApprovedSource.xlsx]Inputs'!$B$2 to
'[WCABReviewSource.xlsx]Inputs'!$B$2. Only xl/workbook.xml changes; the
compact package deliberately has no externalReferences declaration and no
externalLink part. WCAB reads local OOXML only: it does not resolve, open,
fetch, authenticate to, trust, refresh, calculate, or claim that a client
resolves the name or returns a value.

The second new pair records a protected-sheet permission, not an interaction.
Microsoft’s worksheet-protection guidance
lists Sort among the actions a protected sheet can permit, while the Open
XML SheetProtection reference
stores the action controls. The pair retains protected Controls,
Controls!D2=B2*C2, its direct Dashboard!B4=Controls!$D$2 consumer, styles,
calculation properties, and every other action lock. Only raw
sheetProtection/@sort moves from 1 (locked) to 0 (permitted), leaving
xl/worksheets/sheet1.xml as the sole package difference. WCAB does not test a
password, authorization, editable range, client sort operation, or resulting
value.

Version 0.19.0 retains the tool-neutral normalized observation protocol.
An adapter can declare a case analyzed, unsupported, or errored; the scorer then
reports expected-fact recall, coverage-disclosure recall, analyzed coverage,
and agreement with the benchmark's reference review convention. WCAB's facts are deliberately
targeted rather than exhaustive, so an unrecognized observation stays visible
for review instead of being labeled a false positive. Unsupported analysis is
visible too—it cannot become a pass.

Version 0.21.0 retains that protocol and adds a stored list-source transition,
so a tool can distinguish a data-validation control change from a calculated
or future-input result.

A gap between existing benchmarks

Existing resources answer important adjacent questions:

WCAB focuses on a different boundary: reviewing a candidate version against an
approved one. Its contract is explicit change facts, a stated
accept/review/block convention, static impact that should not be missed, and
visible unsupported coverage instead of a silent pass.

Cases that distinguish text changes from semantic risk

The scenarios cover formula-to-value replacement, wrong-period
reference drift, input propagation, external formula references, named-range
redirection, copied-formula interruption, mismatched SUMIFS ranges, removed
input validation, conditional-formatting removal, a stored custom number format
whose code changes from a percentage display to ;;; while its raw value,
style assignment, and dependent formula remain fixed, a stored ignored-error
declaration whose formula-range suppression is added while its ordinary cells
and formula context remain fixed, a stored workbook-structure lock whose
lockStructure flag is disabled while a hidden review sheet and formula context
stay fixed, a cellIs
conditional-formatting threshold whose raw cutoff moves while its target,
priority, operator, values, and differential fill remain fixed,
hidden-sheet visibility,
formula-cell unlocking, incomplete manual calculation, direct static cycles,
3-D formula scope expansion, an Excel Table scope expansion with unchanged
structured-reference text, an introduced INDIRECT reference, unchanged
INDIRECT and OFFSET formulas whose selectors change, structural formula
rewrites, a connection refresh-on-open control, an external-workbook link
update-on-open policy, a relationship-backed QueryTable refresh-on-open control
with fixed connection metadata and saved cells, a relationship-backed worksheet
cell hyperlink whose external target changes while visible text stays fixed, a
local PivotTable-cache refresh-on-open control, a
local PivotTable value field whose aggregate changes from Sum to Average while
its source, cache, and stored report cells remain fixed, a local PivotTable
Slicer cache whose selected Region item changes while its source, cache, and
stored report cells remain fixed, a connection-only Power Query M definition
whose local-table filter literal changes while its source and controls remain
fixed, a stored Scenario Manager alternate input whose raw value changes while
visible worksheet values and formulas stay fixed, a one-variable What-If Data
Table whose raw input reference changes while its output range and ordinary
formulas stay fixed, a list data-validation rule whose stored source moves
between local status ranges while its target and metadata stay fixed, a dashboard chart
whose numeric-series source changes while cells and its other bindings remain fixed, an
unchanged circular formula whose iterative
calculation becomes enabled, an unchanged precision-sensitive input and formula
whose calculation switches to precision as displayed, a saved formula result
that changes without a formula or input edit, a workbook serial-date-system
control change without a cell edit, an active AutoFilter criterion change with
stable SUBTOTAL and downstream formulas, a relationship-backed saved Named
Sheet View criterion change with a stable base AutoFilter and formulas, an XML
Map table-column XPath retargeting with stable map declarations, local bindings,
cells, and formulas, a raw Power Pivot/Data Model relationship target-key
change with fixed local Tables and opaque payload, an unchanged array formula whose
mode changes from legacy CSE to dynamic, and a cross-workbook dependency.

That combination is deliberate. A column insertion can rewrite many formulas
while retaining declared logical inputs. Conversely, inserting a tab inside
SUM(Jan:Mar!B5) can change a formula's scope while its text remains unchanged.
Neither a raw text diff nor a “no formula changed” signal is enough for a
reliable review workflow.

Truth with stated limits

Each case contains truth.json, with facts such as formula_to_value,
sheet_visibility_changed, and three_d_scope_changed. The must_reach
section declares downstream formula locations that a conservative local A1
dependency walk must reach. It does not claim formula evaluation, complete
Excel semantics, dynamic-reference resolution, or numerical correctness.

The project ships a validator that reads the generated workbooks and verifies
the truth contract. It also canonicalizes OOXML ZIP member order and timestamps
so regeneration is byte-for-byte reproducible. Version 0.36.0 passed 248 tests
locally under Python 3.13, and fresh Python 3.13 wheel and source-distribution
installations both validated all 53 cases and reproduced the catalogue
byte-for-byte.

An optional local FormulaFence adapter shows one concrete integration without
making its report schema normative. FormulaFence 0.220.0 recovered all 54
currently mappable facts, all three scoreable dynamic-reference coverage
declarations, and five targeted lint rules. The driver declarations require
both its value_changed record and candidate dynamic_reference_cells profile
feature, rather than an invented target value. For the connection fact, it
requires the exact connection ID and refresh_on_load false-to-true transition
behind FF023. For the external-link policy fact, it requires exactly
update_links: never → always while the other workbook-wide refresh controls
retain their defaults, rather than accepting a generic settings diff. For the
iteration fact, it requires exactly iterate: false → true while the stored
100-iteration / 0.001-delta bounds and all other calculation controls remain
unchanged behind FF009. For the precision-as-displayed fact, it requires
exactly fullPrecision: true → false while the other stored calculation
controls remain unchanged behind FF009. For the saved-result fact, it requires
FormulaFence's matching formula_cached_result_changed record and FF042,
with exactly one unexplained material cache change; the tool deliberately
redacts raw cache values and the formula-cell location. For the date-system
fact, it requires date1904: false → true, explicit
dateCompatibility=true, zero unrecognized controls, and FF117. For the
active-filter fact, it requires the matching redacted
filter_visibility_controls_changed record and FF036; WCAB's raw validator
independently establishes the North → South values and stable formulas. For
the saved-Sheet-View fact, it requires exact named_sheet_views_changed
evidence and FF038: FormulaFence reports one worksheet, part, view, filter,
column, and criterion, with no sort rule, sort condition, or unrecognized
declaration. It redacts the view name, IDs, range, and selected value, so WCAB
independently verifies the stored North → South criterion, base-AutoFilter
relationship, stable formulas, and Named-Sheet-View-part-only boundary. For
the XML Map fact, it requires exact xml_mapping_controls_changed evidence
and FF049: FormulaFence reports one map part, schema, map, data binding,
file binding, table binding, and single-cell binding, with no unrecognized
mapping metadata. It redacts the schema, map, XPath, table, and cell values, so
WCAB independently verifies the NetAmount → TaxAmount transition, stable
declarations and formulas, and the table-part-only boundary. For
the Office Web Add-in fact, it requires exact office_web_addins_changed
evidence and FF028: FormulaFence reports one declared task-pane part, task
pane, web-extension part, and store reference; one hidden locked pane; no
bindings, snapshots, external relationships, in-content references, or
unrecognized parts; and an auto-show count moving from zero to one. It redacts
the IDs, store name, and property value, so WCAB independently verifies the
false-to-true property, stable workbook context, and web-extension-part-only
boundary. For
the embedded-OLE fact, it requires exact
worksheet_embedded_controls_changed evidence and FF029: FormulaFence
reports one worksheet, one embedded OLE object, one internal payload, no
linked/external/ActiveX/VML/presentation material, and no unrecognized
declaration while its auto-load count moves from zero to one. It does not
inspect or deserialize the opaque payload, so WCAB independently verifies the
stored attribute transition, relationship and content-type boundary, inert
bytes, formula context, and worksheet-XML-only package change. For the
QueryTable fact, it requires exact query_table_refresh_controls_changed
evidence and FF023: FormulaFence retains one ImportedData table, connection
ID 1, fixed background/disable/remove/fill/edit/growth controls, name metadata,
and no opaque metadata while only refresh_on_load moves from false to true.
It does not expose an endpoint, OOXML part, or result rows, so WCAB independently
verifies the local relationship graph, fixed connection-level control, saved
cells/formulas, and QueryTable-part-only boundary. For
the cell-hyperlink fact, it requires exact cell_hyperlink_controls_changed
evidence and FF047: FormulaFence retains one external worksheet hyperlink
binding with no location, display, tooltip, or unrecognized declaration while
its binding, definition material, and relationship material change. It redacts
the target and relationship ID, so WCAB independently verifies the reserved
target transition, stable visible text/formulas, and relationship-part-only
boundary. For
the external-workbook source fact, it requires exact
external_link_packages_changed evidence and FF025: FormulaFence retains one
external workbook and source sheet, no DDE/OLE link, no cached external data,
and no opaque metadata while only source_material_changed changes. It redacts
the target and relationship IDs, so WCAB independently verifies the
reserved-target transition, external-reference graph, stable formula context,
and externalLink-relationship-part-only boundary. Its generic
external_relationships_changed / FF063 diagnostic remains deliberately
unmapped. For
the external-defined-name source fact, it requires both the exact
one-surface external_workbook_link_surfaces_changed / FF081 evidence and
matching ScenarioRate defined_name_changed / FF008 evidence for the two
generated source expressions; the surface ledger alone is insufficient. WCAB
independently verifies the local name text, absence of externalLink and
externalReferences declarations, and the workbook-XML-only boundary. For the
sheet-protection sort-permission fact, it requires the exact
sheet_protection_changed / FF022 profile with every action lock retained
except sort; WCAB independently verifies the raw 1 → 0 transition and
worksheet-only package difference. Neither mapping opens a source, tests a
password, performs a sort, or claims a resulting value. For
the named-LAMBDA fact, it requires FormulaFence's exact ScenarioValue
defined_name_changed record and matching FF008 before/after texts; WCAB
independently verifies the local definition, stable inputs/formulas, and the
workbook-XML-only boundary. For the Table calculated-column fact, it requires
FormulaFence's exact redacted table_definition_changed profile,
calculated_column_formula_material_changed, and FF013; FormulaFence does
not expose the master text, so WCAB independently verifies the local Table
binding, raw master transition, stable row/dashboard formulas, and Table-part
boundary. Neither mapping evaluates a workbook or claims a result. For
the Data Model relationship fact, it requires an exact high-severity
power_pivot_data_model_changed record and FF033, with one internal data
part, one workbook binding, one declaration, two model tables, one model
relationship, and a fixed fingerprinted payload profile on both sides. The
adapter redacts model names and keys, so WCAB independently verifies the exact
stored DateKey → FiscalDateKey transition, fixed opaque payload, and
workbook-XML-only boundary. Neither side deserializes a Data Model, evaluates
DAX, refreshes it, calculates or renders a report, or infers model-to-cell
impact. For
the XLM Auto_Open binding fact, it requires FormulaFence's exact
high-severity xlm_automatic_macro_bindings_changed record and FF076, with
one automatic binding, one Auto_Open binding, and no
close/activate/deactivate binding on either side; only its material-change flag
may differ. FormulaFence redacts the macro-sheet name, target cells, XML, and
macro instructions, so WCAB independently verifies the exact stored
$A$1 → $A$2 target transition, fixed two-HALT() macro sheet, very-hidden
state, fixed relationship/content types, and workbook-XML-only boundary.
Neither side opens Excel, enables or executes XLM code, parses or emulates
macro instructions, or claims client behavior. For
the PivotCache fact, it requires pivot_cache_refresh_controls_changed and
FF023, with only refresh_on_load: false → true in FormulaFence's redacted
cache profile; WCAB independently verifies the source and PivotTable bindings.
For the PivotTable aggregation fact, it requires FormulaFence's exact redacted
pivot_table_definitions_changed profile and FF031; FormulaFence does not
expose the selected aggregate, source labels, or a rendered result, so WCAB
independently verifies the local graph, stored cells, and sum → average
declaration.
For the PivotTable Slicer fact, it requires FormulaFence's exact redacted
slicer_timeline_cache_definitions_changed profile and FF032; FormulaFence
does not expose the Slicer name, selected item/value, or a rendered report, so
WCAB independently verifies the local graph and stored North → South
selection.
For the Power Query fact, it requires FormulaFence's exact redacted
power_query_changed profile and FF024; FormulaFence does not expose M
source, local-table values, or a query result, so WCAB independently verifies
the package-root binding, local source, connection-only controls, and stored
North → South literal.
For the Scenario Manager fact, it requires FormulaFence's exact redacted
scenario_manager_changed profile and FF035; FormulaFence does not expose
the scenario name, input cells, values, comment, or user, so WCAB independently
verifies the selected locked scenario and the raw 0.08 → 0.16 stored value.
For the What-If Data Table fact, it requires FormulaFence's exact redacted
one-variable what_if_data_tables_changed profile and FF034; FormulaFence
does not expose the output range, local input references, or calculated table
values, so WCAB independently verifies the generated D3:D5 master, raw
B2 → B3 r1 transition, stable surrounding controls, and worksheet-only
package change.
For the list-source fact, it requires FormulaFence's exact
data_validation_changed rule transition and FF020; FormulaFence exposes
the stored source/control metadata but does not evaluate a list or decide
whether an input is permitted. WCAB independently verifies the local source
bindings and stored transition without executing Excel or Power Query.
For the conditional-formatting threshold fact, it requires FormulaFence's exact
conditional_formatting_changed rule transition and FF021, with the
raw 100 → 50 formula change while the declared target, priority, operator,
values, and differential fill remain stable. FormulaFence does not evaluate a
condition or infer a rendered result; WCAB independently verifies the raw
worksheet rule.
For the custom number-format fact, it requires FormulaFence's exact redacted
number_format_controls_changed profile and FF039. FormulaFence deliberately
redacts the code and target, so WCAB independently verifies the
Operations!B2 transition and the styles-only package boundary.
For the stored ignored-error fact, it requires FormulaFence's exact redacted
ignored_error_controls_changed profile and FF037. FormulaFence deliberately
redacts the target and formula, so WCAB independently verifies the generated
Operations!B5 declaration and worksheet-only package boundary.
For the stored workbook-structure fact, it requires FormulaFence's exact
non-secret workbook_protection_changed profile and FF022: only
lock_structure changes from true to false, all other locks remain false, and
neither side has credential or opaque metadata. WCAB independently validates
the generated raw workbookProtection/@lockStructure transition and
xl/workbook.xml-only boundary.
For the chart fact, it requires FormulaFence's exact one-chart redacted
chart_definitions_changed profile and FF030; WCAB independently verifies
the title, category, and numeric-series source references.
For the array fact, it requires the exact legacy-CSE-to-dynamic mode transition and
stored output range
behind FF018. Its normalized export reports those facts
without inventing review decisions, so its score is 54 of 55 declared facts,
three of three coverage disclosures, full analyzed coverage, and zero policy
agreement. The structural rewrite is intentionally left unmapped: it documents
intent, but does not pretend that a small fixture proves generic Excel semantic
equivalence.

Try it

git clone https://github.com/SybilGambleyyu/workbook-change-benchmark.git
cd workbook-change-benchmark
python -m venv .venv
.venv/bin/python -m pip install -e '.[dev]'
wcab validate --fixtures fixtures
wcab manifest --fixtures fixtures
wcab observation-template --fixtures fixtures --output observations.json
wcab score --fixtures fixtures --observations observations.json
pytest
Enter fullscreen mode Exit fullscreen mode

Read the canonical release note
for the schema, validation record, and release links. WCAB is MIT-licensed and
available on GitHub,
the v0.36.0 release,
and the dataset mirror.

Top comments (0)