Release note · August 3, 2026

A package signature can change scope without changing a cell

By SybilGambleyyu · FormulaFence 0.224.0 and WCAB 0.42.0

A workbook can keep every worksheet cell and ordinary formula stable while the declared scope of a package signature changes. That is material review evidence: signature metadata is part of a workbook's integrity and provenance surface. But it is also an area where reports must be precise. A changed declaration is not proof that a signature is valid, that a certificate is trusted, or that a package consumer will accept it.

The Open Packaging Conventions specification defines package relationships and signature parts. The W3C XML Signature specification distinguishes the references in SignedInfo from references collected in a Manifest. Those distinctions matter: a SignedInfo reference can identify a local signature object, while an Object/Manifest/Reference can declare a package part. A tool should not mistake the former for the latter.

Review the declared scope, not an invented trust result

FormulaFence 0.224.0 reads the bounded OPC signature structure and reports safe aggregate counts: Manifest references, direct part references, relationship selectors, and direct workbook, worksheet, VBA-project, and external-data-connection categories. A selector is treated as declared package scope only when its OPC Relationships Transform is immediately followed by XML C14N, with or without comments. When the bounded coverage changes, it emits high-severity FF050 evidence with:

package_signature_material_changed: true
package_signature_manifest_coverage_changed: true

The report intentionally does not serialize a Manifest URI, selector, digest, signature value, certificate, identity, or trust assertion. Malformed or unsafe declarations are handled conservatively. FormulaFence does not calculate a digest, validate a signature or transform, build a certificate chain, check revocation, apply policy, or predict a consumer decision.

A deterministic fixture that separates the two reference scopes

Workbook Change Assurance Benchmark (WCAB) 0.38.0 introduced a compact direct-part pair for this boundary. The package keeps one root-to-origin relationship, one origin-to-signature relationship, required content types, and a SignedInfo reference to a local #idWCABPackageObject fixed. Its ordinary Controls!B10=12 value and Controls!D10=B10*C10 formula also remain fixed.

The only changed archive member is _xmlsignatures/sig1.xml. Inside that part, one Object/Manifest/Reference/@URI moves from the direct workbook part to the first worksheet part. WCAB's raw validator establishes that package shape and compares the signature XML after erasing only that URI. Its digest and signature values are deliberately synthetic: this is a declaration-scope fixture, not a sample valid signature.

Equal counts can still conceal a material retarget

WCAB 0.39.0 adds the harder companion case. Its Manifest URI remains the root relationships part; it contains one OPC Relationships Transform immediately followed by XML C14N. Only one RelationshipReference/@SourceId moves: from the root office-document relationship to the root signature-origin relationship. The graph, cells, formulas, transform sequence, and every archive member except _xmlsignatures/sig1.xml remain fixed.

Every redacted aggregate stays equal before and after: one Manifest reference, one relationship selector, zero direct-part references, one origin, and one XML signature. FormulaFence therefore must surface the private material difference through package_signature_manifest_coverage_changed: true, not through a changed count or exposed selector. A selected relationship entry is not proof that its target part was signed. Neither WCAB nor FormulaFence executes the transform or makes a digest, signature, certificate, trust, or consumer-decision claim.

A resolved comment can move without a cell change

Modern threaded comments are likewise worksheet-associated package state, rather than ordinary cell values. WCAB 0.40.0 adds a deterministic pair with one top-level synthetic thread whose threadedComment/@done value moves from 0 to 1. The only changed archive member is xl/threadedComments/threadedComment1.xml; comment text, person data, timestamps, cell binding, content types, relationship graph, ordinary values, formulas, and calculation properties stay fixed.

FormulaFence reports the transition only as high-severity threaded_comment_controls_changed/FF045 evidence with a redacted resolved_comment_count transition from 0 to 1. WCAB's raw validator retains the precise synthetic package boundary without placing text, person or comment identifiers, timestamps, cell references, or relationship IDs in its public fact. A stored resolved flag does not prove that someone reviewed or approved work, received a notification, held an identity, or completed an authorization or workflow step.

A revision log can change without a cell change

Legacy shared-workbook revision history is another package surface that can retain a material change after ordinary cells stay stable. Microsoft’s Headers class reference describes revision history as persisted through headers and revision-log parts. WCAB 0.41.0 adds a deterministic pair with one workbook revision-header part, one relationship-backed revision-log part, three revision records, and matching enabled shared-workbook tracking, retention, and history-protection controls.

The only changed archive member is xl/revisions/revisionLog1.xml. Inside it, one synthetic historic old value changes while the workbook-to-header-to-log graph, content types, control profile, revision-record shape, ordinary values, formulas, and calculation properties remain fixed. The raw validator uses that narrow synthetic boundary privately; its public fact carries only the safe equal-count profile, not historic values, cell locations, author data, timestamps, GUIDs, or relationship IDs.

FormulaFence exposes the difference only as high-severity shared_workbook_revisions_changed/FF062 evidence with equal safe counts and revision_log_material_changed: true. A stored revision entry is not proof of provenance, an author’s identity, conflict resolution, review, approval, authorization, workflow completion, or what an Office client will do.

A protected-range descriptor can change without a cell edit

The ISO/IEC SpreadsheetML protected-range example places account descriptors in nested securityDescriptor children. The Open XML SDK's ProtectedRange reference likewise describes that child. It is therefore a stored review surface—but it is not proof that a particular application identified, authenticated, authorized, or permitted an editor.

WCAB 0.42.0 adds a compact standards-form pair. Only one nested protectedRange/securityDescriptor text node changes in xl/worksheets/sheet1.xml; the protected sheet, locked Controls!B2:B2 target, range name/reference, legacy verifier, Controls!D2=B2*C2 formula, and direct Dashboard!B4 consumer remain fixed. The raw validator checks the synthetic element privately and compares the worksheet after erasing only that text. Its public fact excludes the descriptor, range name, and verifier; it does not test a password, encryption, identity, authentication, authorization, editable-range enforcement, client behavior, or a result.

FormulaFence 0.224.0 reports the material change only as high-severity protected_range_permissions_changed/FF022 evidence. Its equal redacted profiles retain one named range, one legacy verifier, one standard descriptor, and no opaque metadata, while security_descriptor_material_changed: true carries the change. It emits no descriptor, account identity, range name, or verifier value.

Two layers, each with a narrower job

The optional WCAB adapter accepts the direct-part fact only when FormulaFence emits the exact high-severity digital_signature_controls_changed record and matching FF050, with one origin, one XML signature, one Manifest direct-part reference, no certificates/VBA signatures/selectors, and an aggregate transition from one workbook part to one worksheet part. For the selector case, it instead requires equal aggregate profiles plus the separate coverage-change flag and FF050. The threaded-comment case requires its exact safe aggregate profile, an explicit definition-material signal, and high FF045 evidence. The revision-log case requires the exact redacted one-header, one-log profile, the material-change signal, and high FF062 evidence. The protected-range case requires high-severity protected_range_permissions_changed and FF022 records, the equal safe range profile, and its descriptor-material signal. WCAB independently checks each bounded raw transition and one-member package boundary that FormulaFence deliberately withholds.

That division lets a production CI artifact describe a meaningful scope transition without becoming a ledger of signature material or a false trust verdict. It also gives a benchmark a concrete way to reject a tool that notices only that a signature part exists while missing what its Manifest now declares.

Evidence before the claim

FormulaFence 0.224.0 passed 1,598 tests, lint, package checks, fresh-install verification, and hosted CI. WCAB 0.42.0 contains 59 cases and 61 observable facts; its 280 tests, fixture validation, fresh wheel/source installs, byte-for-byte manifest reproduction, and hosted Python 3.10/3.13 CI passed. The WCAB dataset mirror is available on Hugging Face.

Use the releases

python -m pip install https://github.com/SybilGambleyyu/formulafence/releases/download/v0.224.0/formulafence-0.224.0-py3-none-any.whl
formulafence diff approved.xlsx candidate.xlsx

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 formulafence-observations --fixtures fixtures --executable formulafence --output /tmp/formulafence-observations.json
wcab score --fixtures fixtures --observations /tmp/formulafence-observations.json

Both projects are MIT-licensed. Read the FormulaFence release, the WCAB release, or the full benchmark note for the underlying contracts and validation records.