fix(consent): stop documenting event-identity tail order - #142
cursor[bot] wants to merge 28 commits into
Conversation
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 then revoke must bind the later-inserted revoke, not a lexicographically larger grant event_ref. Keep the ledger FOR UPDATE lock and fail closed on a grant-only snapshot after that revoke. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Reject a consent/outbox bind when two events share occurrence time and created_at, and require every durable event identity in the submitted ledger so opaque event_ref cannot reopen the #112 grant-beside-revoke hole. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Module rustdoc still described occurrence, insertion, then event identity. That is the exact tie-breaker this slice rejects. Align the rustdoc, prove both tied envelopes fail closed at persist, and cite Helsinki 2024 plus ISO/IEC 29184 and TS 27560 for the consent-record interchange target. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
There was a problem hiding this comment.
Stale comment
Consent/outbox landing vehicle
Head
75d71ebis the correct successor of #70/#112/#120/#123/#134.Verified:
- Durable tail is
ORDER BY occurred_at_unix_ms DESC, created_at DESCwith noevent_refsort key.- Tied
created_atfails closed (tied != 1→InvalidPropagationEnvelope).- Module and function rustdoc no longer say “then event identity”.
- Same-millisecond later revoke beats a lexicographically larger grant.
Do not merge #70, #112, #120, #123, or #134 in parallel.
Residuals for later slices, not this merge:
persist_consent_ledgeris still a public write path; makepersist_consent_ledger_with_outboxthe sole product write after land; ledger reload stays on #140; thenPOST /v1/consents.Independent last-push approval and exact-head required checks remain required. This review does not approve.
Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Prefer this exact head (75d71eb) over #134 at 729a2bd, #123 at 6643041, #120 at 3f72446, #112 at 040bcf7, and #70 at 3180620.
The unique slice matches the code. Module and function rustdoc require a unique durable tail ordered by occurrence time, then insertion time. They mention opaque event identity only to reject it. require_durable_ledger_tail locks consent_ledger, requires every durable consent_event identity in the submitted ledger, orders only by occurred_at_unix_ms DESC, created_at DESC, and fails closed when tied != 1. Persist-level equal_created_at_cannot_bind_either_tied_envelope submits a complete grant+revoke ledger with either tied envelope and leaves integration_outbox empty. ADR-0006 invariants 7–8 cite Helsinki 2024, ISO/IEC 29184:2020, and ISO/IEC TS 27560:2023. NIST Privacy Framework 1.0 stays; 1.1 is still CSWP 40 IPD.
No blocking production defect on this slice. Independent non-author approval and required checks on this unchanged head remain the merge gates. Do not self-approve.
persist_consent_ledger is still a public skip-outbox write path. That is the next product gap after this lands, then purpose-specific POST /v1/consents. Do not fold those onto this head. Do not merge the predecessor outbox PRs in parallel.
Reload remains independent #140. Session HTTP remains #149 and must not stack here.
Sent by Cursor Automation: Fix Issues
| - `consent_ledger` and `consent_event` persist the already-merged `src/consent.rs` append-only ledger through `migrations/0005_consent_lifecycle.sql`. Active PR #142 composes those existing rows with `integration_outbox` in one caller-owned transaction after locking the participant ledger, requiring the submitted ledger to contain every durable event identity, and selecting a unique durable tail by occurrence time then `created_at`; it adds no new physical objects. HTTP consent transport and derived snapshot tables remain Target. | ||
| - `participant_identity_link` is the persistence target accepted by ADR-0020. The current `src/participant.rs` `keyverse_subject_ref` field is an application-domain first-link projection, not the future mutable persistence source of truth. | ||
| - `longitudinal_enrollment`, `longitudinal_observation_record`, and `temporal_analysis_submission` make the ADR-0008 Commons-owned Gyeot/TEPP orchestration boundary explicit. No TEPP analytical kernel is duplicated here. | ||
| - `integration_outbox`, `integration_delivery_attempt`, `integration_inbox`, and `integration_consumption` reflect `src/integration.rs` domain semantics. Outbox/inbox/delivery-attempt tables are on protected main; `integration_consumption` pending/processing/completed/quarantined persistence and expire-and-reclaim of a crashed processing claim exist only on this Active PR until merged. |
There was a problem hiding this comment.
This adjacent sentence still says integration_consumption exists only on this Active PR. Protected main already ships migrations/0012_integration_consumption.sql and TRACEABILITY maps that adapter as implemented. Leave it off this consent/outbox head; correct the leftover on a docs-only follow-up after #142 lands.
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.
|
Scheduled review-feedback autofix for this PR head.
|
|
Scheduled review-feedback autofix for this PR head.
|


Why
#134 fails closed when two durable
consent_eventrows shareoccurred_at_unix_msandcreated_at. Its module rustdoc still said the tail was ordered by occurrence time, insertion time, then event identity. That is the exact opaque-key tie-breaker this slice rejects.Prefer this head over #134 at
729a2bd. Do not merge #70 at3180620, #112 at040bcf7, #120 at3f72446, #123 at6643041, or #134 at729a2bd.What
Successor of #134 on the same fail-closed tail:
integration_outboxempty.This does not change consent purposes, infer consent, duplicate identity credentials, or weaken research opt-in separation.
Verification
cargo test --lib postgres_consent_propagation::envelope_testscargo test --test documentation_architecture_contract --test traceability_active_pr_contractcargo clippy --all-targets -- -D warningsNext after this lands: make
persist_consent_ledger_with_outboxthe sole product consent write path, then purpose-specific consent HTTP (POST /v1/consents).Independent non-author approval and required checks on the unchanged exact head remain merge gates. Never self-approve.