Release note · August 4, 2026
A lock declaration is not an effective lock
Word content controls can carry a direct stored lock declaration. That is useful evidence during a document handoff: it says what the package declares. It does not prove that a particular client will honor it, that a user cannot alter the control, or that an absent declaration means an unlocked effective state.
DocFence 0.39 makes this narrow stored fact review-visible. It inventories only the direct w:sdtPr/w:lock child of each supported Word story's content control, and it keeps the output aggregate-only: no content-control text, tag, numeric ID, XML path, or raw lock value is emitted.
Read the declaration, preserve the uncertainty
The Open XML SDK defines w:lock as the content-control lock element and exposes four schema values: unlocked, sdtLocked, contentLocked, and sdtContentLocked. DocFence validates that exact direct shape and reports five counts:
- controls with no direct lock declaration;
- the four explicit schema states.
That first category matters. An omitted leaf is not silently recast as unlocked; the underlying contract has control-type-specific behavior. The inventory is a static package observation, not an interpretation of a client or a security guarantee.
When the count totals are unchanged but control assignments are exchanged, DocFence still reports content_control_lock_inventory_changed. The comparison uses private local references only to preserve that review signal; those references never become public report fields.
Policies with an explicit scope
A clean-handoff policy can require every discovered content control to carry an explicit, non-unlocked declaration:
rules:
require_content_control_locks: true
This candidate-state check emits DFP090. A controlled template can instead reject any material inventory change from an approved baseline:
rules:
no_content_control_lock_changes: true
DFP091 reports the baseline-to-candidate change. Neither rule opens Word, applies protection, authenticates an editor, renders a document, or predicts what a client will enforce.
A paired static-review case
DCAB 0.29.0 adds its 40th deterministic paired-WordprocessingML case. The new synthetic pair keeps the package-member set, content-control ID, tag, and stored text fixed. Only the direct declaration changes from w:lock w:val="unlocked" to w:lock w:val="sdtContentLocked".
The independent fixture validator checks the fixed carrier and exact legal declaration states without using DocFence. The optional adapter then recognizes only DocFence's public aggregate transition. The 40-case dataset mirror carries the same manifest and fixture bytes.
Released and verified
Tagged CI passed for DocFence and DCAB, including the benchmark adapter installed from the DocFence tag. A full 40/40 benchmark run reached complete fact and reference-policy agreement. Fresh wheel and source builds from the tags passed package validation, and public GitHub downloads were byte-compared against those builds. The updated dataset manifest and new fixture files were downloaded and byte-compared as well.
python -m pip install https://github.com/SybilGambleyyu/docfence/releases/download/v0.39.0/docfence-0.39.0-py3-none-any.whl
docfence profile candidate.docx --format markdown
docfence check approved.docx candidate.docx --policy docfence.yml --format sarif --output docfence.sarif
The public release includes a wheel and source archive. The tagged policy reference, threat model, and validation notes define the exact behavior and privacy limits.