Skip to content

fix(consent): order durable outbox tail by insert time - #123

Closed
cursor[bot] wants to merge 25 commits into
mainfrom
cursor/bc-192db1c0-e3c9-467d-b733-0ebc1a929b83-e110
Closed

cursor[bot] wants to merge 25 commits into
mainfrom
cursor/bc-192db1c0-e3c9-467d-b733-0ebc1a929b83-e110

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Why

PR #112 binds the consent outbox to a durable ledger tail, but that tail is ORDER BY occurred_at_unix_ms DESC, event_ref DESC. ConsentLedger::record allows equal timestamps. A same-millisecond research revocation whose event_ref sorts before the grant is hidden, so a grant-only snapshot can still enqueue grant propagation beside stored revocation. A complete same-millisecond revoke envelope is also rejected when the grant identity sorts last.

#70 at 3180620 only checks the in-memory last event. Do not merge #70 or #112.

What

Successor to #70/#112 on current main (a763735) plus the #112 composition:

  • After ledger persist, lock consent_ledger FOR UPDATE.
  • Require every durable consent_event identity to appear in the submitted ledger.
  • Order the durable tail by occurred_at_unix_ms DESC, created_at DESC, event_ref DESC (created_at already exists; no new physical objects).
  • RED: persist same-millisecond grant+revoke where the grant identity sorts last, then retry a grant-only snapshot + grant envelope → InvalidPropagationEnvelope, outbox count unchanged.
  • Happy path: the later-inserted same-millisecond revoke commits with its bound outbox.
  • TRACEABILITY, CHANGELOG, ERD, UML, AS_BUILT, and ADR-0015 now name this successor and tell agents not to merge feat(consent): persist consent change with bound outbox atomically #70/fix(consent): bind outbox to durable ledger tail #112.

This does not change consent purposes, infer consent, duplicate identity credentials, or weaken research opt-in separation.

Verification

  • cargo test --test postgres_consent_outbox_latest_event --test postgres_consent_outbox_transaction --test postgres_consent_outbox_error_boundary
  • cargo test --lib postgres_consent_propagation
  • cargo clippy --all-targets -- -D warnings
  • full Runtime CI, exact statement/branch coverage, docs, security and independent review on the exact head

Next after this lands: make persist_consent_ledger_with_outbox the sole consent write path, then purpose-specific consent HTTP.

Open in Web View Automation 

seonghobae and others added 22 commits August 14, 2026 18:15
Reject a grant-only snapshot after a stored revocation by locking the
participant ledger and requiring the durable event tail to match the
envelope before enqueue. Record the Active PR composition in
traceability, changelog, ERD, UML, and as-built schema.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Exercise missing ledger or event rows, time and identity mismatch,
timestamp overflow, and dropped relations so the tail lock stays at
full statement and branch coverage.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Same-millisecond grant/revoke pairs are legal. Ordering the durable tail
by event_ref DESC let a lexicographically later grant hide a stored
research revocation. Require the submitted ledger to contain every
durable event and order the tail by occurrence time, created_at, then
event_ref. Do not merge #70 or #112.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor
cursor Bot requested a review from seonghobae August 16, 2026 15:37
cursoragent and others added 2 commits August 16, 2026 15:37
Point TRACEABILITY, ERD, UML, and AS_BUILT at #123 so schedulers do not
merge #70 or #112.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Record GDPR, NIST Privacy Framework, Helsinki, outbox, and
clock_timestamp references. #120 orders the tail by created_at but does
not require the submitted ledger to contain every durable event.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Review — #123 6643041

Durable-set completeness is the right successor to #120: after FOR UPDATE, every stored consent_event identity must appear in the submitted ledger, so a grant-only snapshot cannot enqueue beside a later revoke even when event_ref DESC would pick the grant.

Residual hole remains: the tail query still ends with event_ref DESC. A complete ledger plus stale grant envelope still succeeds when two rows share occurred_at_unix_ms and created_at and the grant identity sorts last. Fail closed when more than one row ties for max (occurred_at_unix_ms, created_at) instead of using opaque identity as a correctness key.

Do not merge #70, #112, or #120. Do not self-approve. Prefer a successor that keeps this completeness check and rejects an ambiguous physical tail.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
Point TRACEABILITY, ERD, UML, and AS_BUILT at #134 so agents do not land
#70, #112, #120, or #123.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — #123 6643041

Durable-set completeness is correct: after consent_ledger FOR UPDATE, every stored consent_event identity must appear in the submitted ledger, so a grant-only snapshot cannot enqueue beside a later same-millisecond revoke.

Residual hole remains on this head: the tail still ends with event_ref DESC. When two rows share occurred_at_unix_ms and created_at, a complete ledger plus grant envelope can still bind the grant. That fail-closed unique-tail contract is #134 (729a2bd), not this SHA.

Do not merge #70 (3180620), #112 (040bcf7), #120 (3f72446), or this #123 head while #134 is the tighter successor. Independent non-author approval and green required checks are still required on the landing SHA. Do not self-approve.

Next after the landing consent/outbox head: make persist_consent_ledger_with_outbox the sole product write path (authorize ManageOwnConsent before any durable write), then purpose-specific POST /v1/consents.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Protected-main #81 requires claim_deadline_at on processing
integration_consumption rows. Recovery fixtures that omit the column
fail constraint integration_consumption_claim_deadline_shape.

Copy link
Copy Markdown
Contributor

Closing this Draft as superseded by the current #142 consent/outbox landing. #123's own current contract still orders a tied durable tail by event_ref DESC; its independent review says that opaque-identity tie-break can select stale grant propagation when occurred_at_unix_ms and created_at are equal, and explicitly directs the landing to the tighter #134 successor. Current #142 carries that successor line: it requires every durable consent-event identity to appear in the submitted ledger, orders the tail only by occurrence time then physical insertion time, and fails closed when the physical tail is not unique instead of using event_ref as a correctness key. #142's current head remains Draft and has a separate unresolved documentation-drift thread; this closure neither resolves that thread nor transfers approval/check evidence or promotes #142 to protected-main truth. It only removes #123 as the known-weaker competing consent/outbox landing.

@seonghobae seonghobae closed this Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants