Build a controlled weekly learning aggregate with explicit permission, bounded contribution, a release ledger and deletion tests.
Project: design a bounded learner-activity release
Create the protected packet
Define learner as the protected unit and weekly qualifying completion as the statistic. Include several events from one learner, a withdrawn learner, an opted-out learner and a duplicate event. Record training and aggregate-release permissions separately. Contribution bounds should reduce one learner to at most one weekly contribution under the chosen contract.
Separate controls from guarantees
Implement deduplication, permission filtering and a transactional release-budget reservation. Use an approved privacy mechanism if the project claims differential privacy; otherwise label the output a bounded internal aggregate without a formal guarantee. Document sensitivity, unit, composition and the number of planned releases before any external publication. The fixture code below is an eligibility check, not a private mechanism.
Exercise operations
Issue two planned ledger reservations and reject a third that exceeds the approved limit. Repeat a request using the same release ID and return the existing approved result rather than drawing a fresh answer. Withdraw learner L-47 during a batch run and verify the tombstone prevents a stale worker from restoring that learner in the next snapshot.
Submit a review packet
Deliver threat model, field inventory, purpose rules, lineage map, bounded-count fixture, budget ledger, deletion-race test and utility report. If using a formal privacy library, include its mechanism configuration and independently reviewed accounting output. State what a membership probe did and did not test. No deployment claim should exceed the evidence in the packet.
Implementation
def eligible_learner_count(event_rows, withdrawn_ids):
withdrawn = set(withdrawn_ids)
learners = {row["learner_id"] for row in event_rows
if row["learner_id"] not in withdrawn
and row["aggregate_permission"] and row["qualifies"]}
return len(learners)
assert eligible_learner_count([{"learner_id": "L-47", "aggregate_permission": True, "qualifies": True},
{"learner_id": "L-47", "aggregate_permission": True, "qualifies": True}], set()) == 1Performance and operating cost
A scan over N events costs O(N + W) expected time and O(U + W) memory for U eligible learners and W withdrawals. The result is bounded by one contribution per learner in this fixture; it is not differentially private without a reviewed release mechanism.
Common Mistakes
- Do not call a bounded raw count differentially private.
- Do not permit an unregistered fresh release after the budget is spent.
- Do not let a stale batch republish a withdrawn learner.
Read next
- Privacy units and bounded contribution: count people, not events
- Private aggregate releases: sensitivity, budget and query control
- Privacy operations: deletion lineage, access gates and release review
- Project: release receipt triage with lineage, canary checks and rollback
Continue the workflow: Project: federated depot model release review.
