Problem/Motivation

Content locks and preconditions protect some governed writes, but the invariant is not proven across every enabled write channel. A second channel must not bypass an active lock or silently overwrite a concurrent human or agent change.

Proposed resolution

Centralize lock and version-precondition enforcement at the source-of-record boundary used by every governed write path.

Acceptance criteria

  • Every governed entity mutation checks the same active lock and version-precondition contract.
  • A conflicting lock or stale version fails before mutation with a stable reason code.
  • Lock ownership is bound to the authenticated actor/delegation context rather than caller headers.
  • Retries are idempotent and cannot convert a conflict into an overwrite.
  • Tests cover all enabled write transports/tools, relationship-only writes, translations, revisions,
  • concurrent writes, and lock expiry/release.

  • The execution receipt records the checked precondition and final target version.

API changes

All governed write results expose a consistent conflict/precondition failure contract.

Comments

jmcerda created an issue. See original summary.

jmcerda’s picture

Public working mirror: GitHub #108.

Drupal.org remains the authority for this work item; implementation discussion and pull-request linkage may occur in the mirror.

jmcerda’s picture

Status: Active » Fixed

Merged to 1.x: https://github.com/Wilkes-Liberty/mcp_sentinel/pull/120 (merge 7d27618). Ships in the next release.

Lock and version-precondition enforcement now lives at one boundary every governed write channel runs — JSON:API, GraphQL, the Tool plugins, and direct saves, covering relationship-only writes, translations, and deletes (bulk delete included, which previously skipped locks). An active lock held by a different server-resolved principal denies the write and the delete with a stable reason; the acting principal's own lock never blocks it. A save whose loaded copy is no longer the stored default revision is refused instead of overwriting the concurrent change, and every retry of a conflict fails identically without mutating anything. Validated seams get a 422; the unvalidated seam aborts after writing evidence that survives the storage rollback. Passing governed updates record an execution receipt (checked lock state, loaded revision, final target revision) on the audit row, which now writes only after the save commits. Ungoverned human traffic is never gated.

Coverage: twelve new kernel tests across the matrix (both seams, ownership, expiry/release, staleness with concurrent-edit survival, forward-draft continuation, relationship-only, translations, delete, ungoverned); full suite 553 tests / 5053 assertions green on Drupal 10.6 and 11.3 in CI.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • jmcerda committed fe7d443b on 1.x
    Issue #3616541: Enforce governed content locks and version preconditions...

  • jmcerda committed af7b35fd on docs/3616541-readme-boundaries
    Issue #3616541: Tighten the stale-version wording to the default-...

  • jmcerda committed af7b35fd on 1.x
    Issue #3616541: Tighten the stale-version wording to the default-...

  • jmcerda committed 47ab984b on 1.x
    Issue #3616541: Document the write-precondition boundary and the...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.