chore(openspec): rewrite publication-clearance-via-anonymise as event-driven#149
Open
rjzondervan wants to merge 2 commits into
Open
chore(openspec): rewrite publication-clearance-via-anonymise as event-driven#149rjzondervan wants to merge 2 commits into
rjzondervan wants to merge 2 commits into
Conversation
…-driven
The original spec proposed extending the anonymise endpoint with a
parallel `unredactedEntities[]` field as the trigger for consent
creation. Two flaws on review:
1. Two decision channels for the same operator decision. Wave 1.3
(entity-relation-grondslagen) already gives operators a per-entity
primitive via skipAnonymization on the EntityRelation row.
Operationally "publish this entity unredacted" IS skipAnonymization=true;
a parallel unredactedEntities[] just duplicated the surface.
2. 422-on-every-first-anonymise for WOO-pending entities. Putting
consent creation at anonymise time meant every entity needing the
standard 28-day WOO workflow would 422 on first try, leaving
operators unable to run anonymise without first running anonymise.
The 28-day clock has to start at decision time, not at anonymise
time.
This rewrite collapses the two channels and moves the trigger to the
new EntityRelationDecisionUpdatedEvent (OR — added in PR #1503's
amend). DocuDesk's listener catches skipAnonymization false→true and
calls createConsentRequest synchronously. PolicyMatchService inside
createConsentRequest handles all three outcomes: standing-consent
match → auto-resolved consent_given; no match → pending +
objectionDeadline computed now (28-day clock starts at decision
time); prohibition match → PolicyRejectedException → listener
reverses the PATCH + dispatches a Nextcloud notification.
The anonymise endpoint shape is unchanged. It gains a defensive
runtime check: 422 only when a skip-marked relation has a pending
consent record whose objection window is still open. Closed-window
and auto-resolved cases proceed normally.
createConsentRequest becomes idempotent on (documentId, entityKey)
for scope=document records, so multiple relations for the same
entity (multiple text positions of "Jan Janssen") collapse to one
consent record.
Five spec files rewritten:
- proposal.md: new framing; out-of-scope list updated.
- design.md: 7 decision sections (event-driven trigger; idempotency;
three PolicyMatchService outcomes; PATCH reversal + notification
for prohibition matches; defensive anonymise-time check with
blocking-state truth table; widget client-side prohibition
pre-check; notification dispatch stubbed).
- tasks.md: restructured around the new architecture (event listener;
new PolicyRejectedException + BlockingConsentException; ConsentService
idempotency; defensive check; widget pre-check; tests; docs).
- specs/anonymization/spec.md: slimmed to just the defensive runtime
check with the full blocking-state truth table.
- specs/consent-management/spec.md: six new requirements covering
listener subscription, idempotency, the typed exception, PATCH
reversal, generic-failure handling, notification stub.
Hard deps: OR PR #1503 (the event class) + DD PR #147
(entity-publication-policies — PolicyMatchService). No code change
yet — that lands when both deps merge.
openspec validate clean.
Contributor
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-05-15 09:26 UTC
Download the full PDF report from the workflow artifacts.
…/rewrite-publication-clearance-via-anonymise # Conflicts: # openspec/changes/publication-clearance-via-anonymise/design.md # openspec/changes/publication-clearance-via-anonymise/proposal.md # openspec/changes/publication-clearance-via-anonymise/tasks.md
Contributor
Quality Report — ConductionNL/docudesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 108/108 | |||
| npm | ✅ | ✅ 529/529 | |||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/10 statements)
Quality workflow — 2026-05-19 03:23 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rewrites
openspec/changes/publication-clearance-via-anonymiseto use the event-drivenEntityRelationDecisionUpdatedEventfrom OpenRegister PR #1503's amend as the consent-creation trigger, instead of the originally-proposedunredactedEntities[]field on the anonymise endpoint.Why the rewrite was needed. Two flaws in the original proposal:
entity-relation-grondslagen) already gives operators a per-entity primitive viaskipAnonymizationon theEntityRelationrow. Operationally "publish this entity unredacted" ISskipAnonymization: true; a parallelunredactedEntities[]array just duplicated the surface.New design
OCA\OpenRegister\Event\EntityRelationDecisionUpdatedEvent(added in OR PR #1503's amend). DocuDesk's listener catchesskipAnonymization: false → trueand callsConsentService::createConsentRequest()synchronously.PolicyMatchServiceinsidecreateConsentRequesthandles all three outcomes — standing-consent match auto-resolves toconsent_given; no-match starts the 28-day clock at decision time withpending; prohibition match throwsPolicyRejectedException, which the listener handles by reversing the PATCH and dispatching a Nextcloud notification to the acting user.createConsentRequestbecomes idempotent on(documentId, entityKey)forscope: "document"records, so multiple relations for the same entity (multiple text positions of one person) collapse to one consent record.PolicyRejectedException(consent service → listener, signals prohibition) andBlockingConsentException(service → controller, drives the 422 mapping).Files
Five spec files rewritten:
proposal.md— new framing; out-of-scope list updated.design.md— 7 decision sections (event-driven trigger; idempotency; threePolicyMatchServiceoutcomes; PATCH reversal + notification for prohibition matches; defensive anonymise-time check with blocking-state truth table; widget client-side prohibition pre-check; notification dispatch stays stubbed).tasks.md— restructured around the new architecture.specs/anonymization/spec.md— slimmed to just the defensive runtime check with the full blocking-state truth table.specs/consent-management/spec.md— six new requirements covering listener subscription, idempotency, the typed exception, PATCH reversal, generic-failure handling, notification stub.Dependencies
EntityRelationDecisionUpdatedEventclass.entity-publication-policies) — providesPolicyMatchService.Test plan
openspec validate publication-clearance-via-anonymiseclean.EntityRelationDecisionUpdatedEvent,EntityRelationMapper::updateDecisionMetadata,PolicyMatchService::match,ConsentService::createConsentRequest).