DataTalksClub / DataTalksClub/website

Preserve graduates, certificates, and historical Wrapped records

Open
#58 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accessibility admin courses data-migration enhancement frontend integration operations P0 security testing
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Parent epic: #5. Downstream certificate-delivery adapter: #233. Final management parity: #59. Production-like rehearsal: #60.

PM disposition

GROOMED / P0 / DEPENDENCY-BLOCKED. Do not dispatch engineering.

This issue owns the certificate/graduate/Wrapped domain contract. It does not own template publication, recipient-preference evaluation, EmailDelivery, Relay transport, Datamailer migration, live provider access, or sender activation. The implementation must remain dormant behind an explicit application-service boundary wherever a certificate transition requires delivery; no adapter may enable that mutation until the accepted #233 composition can atomically consume its trigger through #49.

No issue text, local fixture, accepted source commit, or green test grants authority to inspect protected production data, execute a production migration/export, call Relay/Datamailer, or send email.

Normative authority

Outcome

Replace the legacy Enrollment-only certificate_url state with one explicit cohort-scoped certificate aggregate and immutable lifecycle history, while preserving every valid legacy certificate destination/alias and historical learner route. Graduate eligibility/export consumes accepted cohort totals and the shared masked Enrollment/export boundary. Historical Wrapped remains readable with its existing JSON interpretation; recalculation is retained only where the characterized input/schema contract is supported.

All mutations use courses application/domain services shared by compatibility adapters, Studio, jobs, and the admin API. Adapters never infer state from a URL, mutate models directly, send email, or create a second certificate/version rule.

Exact certificate identity and lifecycle

Aggregate and version identity
  • There is at most one current Certificate aggregate per accepted cohort Enrollment. It has a stable target UUID, exact Course/Cohort/Enrollment identity, current lifecycle state, current lifecycle version, concurrency revision, and immutable creation identity.
  • Absent means no Certificate row. Persisted states are exactly issued and revoked; blank URL, account profile state, score changes, or missing delivery status never silently change the state.
  • version is a positive, monotonically increasing certificate lifecycle sequence and is distinct from the mutable-resource concurrency revision. Every effective learner-visible lifecycle transition creates exactly one immutable version/history row. (certificate_uuid, version) is unique and never reused or renumbered.
  • Each issued version snapshots the accepted certificate/display name and retained artifact/public-destination identity used for that issuance. Later account/profile, Enrollment, score, template, or URL changes never rewrite an older version. The #23 erasure decision for an already-issued opted-in real name remains binding.
  • Records are archived/retained under the accepted educational-record policy; ordinary operators cannot physically delete history. #64 owns the shared rights/tombstone/restore workflow, which this issue registers with but does not duplicate.
Allowed transitions
  1. Issue: absent → issued version 1 after the accepted #57 eligibility result. This is the only first issue.
  2. Replace/reissue while issued: issued → issued at version + 1 when the learner-visible certificate artifact/destination or certificate-name snapshot changes. This is a reissue for lifecycle and message-purpose identity; it is not a silent in-place URL update.
  3. Revoke: issued → revoked at version + 1, with the current issued artifact no longer presented as active. A bounded reason, accepted high-risk proof, explicit confirmation, authorization, and audit are required.
  4. Reissue after revocation: revoked → issued at version + 1 with a new immutable issued snapshot and the same stable Certificate UUID.

An exact idempotent replay returns the original transition result and version. Reusing the same idempotency key with any changed Course/Cohort/Enrollment, expected state/version/revision, artifact/destination, name snapshot, transition, or reason digest returns a safe conflict. Concurrent first issue, replacement, revoke, and reissue serialize to one accepted version; stale state/revision fails without a partial history row. Issue of an already-issued certificate, revoke of absent/already-revoked state, or reissue of a non-revoked state is rejected unless it is the exact recorded replay. Operator-note/audit-only correction does not create a certificate version.

Eligibility is an accepted #57 cohort-scoped result, not recomputed ad hoc by an adapter. Later score changes do not silently revoke or rewrite a certificate. Any exceptional eligibility repair must use the accepted high-risk repair capability/reason/audit contract and still produce a normal explicit transition; it cannot mutate history.

Trigger contract owned here, transport owned elsewhere

The transition primitive runs inside a caller-owned database transaction, performs no network call, and returns one immutable typed CertificateTransitionResult. For issue, revoke, and either form of reissue it contains exactly one message-trigger disposition and scalar DTO. Exact replay returns the same trigger identity; validation/conflict/rollback returns no trigger.

The stable trigger identity is derived from (certificate_uuid, certificate_version, transition) and is never based on request index, email, mutable URL alone, or transport attempt. The DTO contains only:

  • trigger key and transition (issued, revoked, or reissued);
  • Certificate UUID and lifecycle version;
  • Course, Cohort, Enrollment, and durable learner/account UUID references;
  • the accepted public certificate destination/path for issued/reissued, absent for revoked; and
  • the minimum non-secret course/certificate scalar context explicitly approved by the frozen consumer schema.

It contains no raw recipient email, mutable template body, template key/version, sender, preference result, rendered content, credential, delivery/provider ID/state, Datamailer identity, or transport retry instruction. #48 owns the immutable Relay template/version consumer boundary. #49 owns recipient/preference handling, the unique logical delivery intent and durable job, request-hash/idempotency, redacted status, ambiguity, callback, and reconciliation behavior.

#233 is the first bounded consumer: after #58, #48, and ordinary #49 are accepted, its certificate-purpose application service opens one transaction, invokes this transition primitive, and consumes the returned trigger through #49 before commit. Certificate state, one required logical delivery intent, and one durable job therefore commit together or all roll back; only a leased worker calls Relay afterward. CMP's synchronous /certificates/notify endpoint, caller-managed notify loop, return flag as a delivery receipt, and request/on_commit Datamailer fanout are explicitly forbidden.

Until that composition is integrated, compatibility/Studio/admin adapters must not activate a message-requiring #58 mutation. Deterministic #58 service/migration work may be accepted independently; merging it grants no live delivery or provider authority.

Graduate and export boundary

  • Graduate eligibility and completion totals consume the exact accepted #57 service/result identity. Certificate presence alone does not create eligibility, and a later certificate transition does not rewrite historical score evidence.
  • #245 owns the authorized, masked Enrollment query/export infrastructure, artifact lifetime, formula neutralization, PII capability, and audit boundary. #58 contributes only the graduate/certificate-specific selection, columns, lifecycle fields, and reconciliation semantics through that interface; it creates no second export policy or storage path.
  • Full learner identity/certificate destination is separately capability-gated. Scope applies before lookup/count/export, and out-of-scope/missing results are indistinguishable. Bulk issue/revoke/reissue and graduate export are bounded operations with exact scope/count preview, high-risk confirmation/reauthentication, idempotency, per-row results, audit, and safe partial/crash recovery.

Historical Wrapped contract

Retain the accepted WrappedStatistics and UserWrappedStatistics year/user identity, visibility, totals, rank/display name, course records, certificate counts, and stored JSON interpretation. Existing historical rows render read-only through their retained routes, including supported legacy Course/Cohort references. Unknown/malformed shapes are inventoried and receive an explicit render, quarantine, or owner-approved unsupported disposition; they are never guessed or silently normalized.

Retain recalculation only for a frozen supported schema/input version and exact cohort-scoped inputs. It is bounded, idempotent, side-effect-free with respect to certificates and email, and compares recomputed totals/checksums to stored history. An unexplained difference blocks activation; no new Wrapped generation or redesigned statistics are introduced.

Migration and reconciliation identity

Repository delivery uses synthetic fixtures only. The full protected production-like import/rehearsal remains #60.

The engineer handoff must freeze and record:

  • current base/head full SHAs and change-selective graph/plan/report digests;
  • adopted source repository pin 98a235283904b4ef9ad29e196298540756cf1bcc, #30 copied-file manifest and integration-patch-ledger digests;
  • exact prerequisite merge SHAs, migration leaves, and schema/migration-plan fingerprint;
  • #51 Course/Cohort and legacy-ID/alias mapping fingerprint consumed through #53/#244;
  • #244 Enrollment identity/preservation fingerprint and #245 masked query/export schema fingerprint;
  • #57 eligibility/score result schema and characterized fixture checksum;
  • certificate transition/trigger schema version plus capability-registry/OpenAPI fingerprints; and
  • a Wrapped shape/version inventory digest and supported-recalculation input/checksum policy.

Migration dry-run/apply/exact replay reports safe source/target counts, stable legacy-ID→UUID mapping cardinalities, absent/issued/revoked/version counts, duplicate/missing/ambiguous Enrollment relations, blank/nonblank destination distinctions, certificate-name conflicts, Wrapped shapes/dispositions, transformed/rejected/quarantined rows, and non-reversible aggregate checksums. It preserves valid legacy primary-key/route/alias evidence. Unknown Course/Cohort/Enrollment mapping, duplicate logical certificate, conflicting name/destination, malformed history, or checksum drift fails closed. Import/replay creates no trigger, EmailDelivery, job, Datamailer/Relay call, or other network side effect.

Dependencies and readiness

Accepted policy/baseline inputs: #23, #28, #30, and #31.

Engineering hard-depends on accepted and integrated:

  1. #52 — exact Course/Cohort staff assignment plus function/object/field authorization and high-risk audit boundary;
  2. #53 — final Cohort lifecycle, canonical/legacy route and alias identity, archive protection, and duplication exclusions;
  3. #244 — final cohort-isolated Enrollment identity, preserved certificate/display/history fields, learner dashboard/history access, and migration mapping;
  4. #245 — accepted masked Enrollment query, certificate-state field, PII/export capability and bounded artifact/operation interface; and
  5. #57 — accepted cohort score/completion/eligibility totals and reconciliation result.

#224/#51 are transitive through #53/#244. #32/#33 are transitive through #52/#245. #230/#231/#234/#242/#243/#247/#248/#288 are transitive through #244/#245. Broad parent #54 is a coordination epic, not a dependency. Open needs grooming/decision state on a transitive prerequisite still blocks its accepted consumer interface; it is never inferred from a local candidate.

#48, #49, #50, and #233 are not #58 source dependencies. #58 must not invent their template, delivery, preference, transport, or migration behavior. They are the downstream enablement chain described above. #59 consumes accepted #58 services for final Studio/admin-API completeness. #60 consumes #58 migration/compatibility evidence for the side-effect-disabled production-like rehearsal. #64 remains the shared legal-rights boundary.

Readiness remains GROOMED / DEPENDENCY-BLOCKED. After all five hard inputs are integrated on one green current-main base, PM revalidates every frozen identity above and the #233 handoff before an isolated engineer lane starts. Any dependency, migration, schema, capability, trigger, or source-pin drift returns the issue to PM.

Acceptance criteria

  • One stable Certificate aggregate per cohort Enrollment enforces absent/issued/revoked state, distinct monotonic lifecycle version and concurrency revision, immutable history, and no physical ordinary deletion.
  • Issue, issued replacement/reissue, revoke, and revoked reissue obey the exact transition table; replay/concurrency/stale/conflict behavior creates at most one version and never partially updates current/history state.
  • Eligibility consumes the accepted #57 result, cohort isolation holds, later score/profile changes do not rewrite issued history, and high-risk repair/mutation controls are enforced and audited.
  • Legacy certificate IDs/destinations/routes/aliases and certificate-name evidence map exactly to Course/Cohort/Enrollment/current version, while duplicate/missing/ambiguous/conflicting input fails closed with safe reconciliation evidence.
  • The transaction-scoped trigger result has one stable (certificate UUID, version, transition) identity, the exact allowlisted scalar schema, no transport/PII/template leakage, identical replay, and zero trigger on failure/import/rollback.
  • Graduate-specific selection/export consumes #57 and #245 without a second authorization/masking/artifact policy; bounds, formula safety, PII gating, idempotency, audit, partial failure, and cohort isolation hold.
  • Historical Wrapped renders the frozen JSON interpretation, every unsupported shape has an explicit disposition, and retained recalculation is versioned/idempotent/bounded with unexplained drift blocking activation.
  • Shared services and dormant adapter seams have Studio/admin-API/compatibility capability, result, error, revision, idempotency, audit, and OpenAPI parity without direct model mutation or network delivery.
  • Synthetic migration dry-run/apply/replay and rollback-safe expand state preserve stable identities and report all required counts/mappings/checksums/dispositions with zero outbound work; protected rehearsal remains #60.
  • Focused model/service/migration/API/security/concurrency/adoption tests and graph-selected full verification pass on one frozen candidate; a separate tester inspects all required desktop/mobile learner/operator/Wrapped screenshots before PM acceptance.

Required verification scenarios

  1. Eligible/ineligible/boundary/recomputed learner; sibling Cohorts; first issue; exact/changed replay; concurrent first issue; replacement; revoke; reissue; stale version/revision; invalid transition; repair authorization; rollback at each write boundary.
  2. Legacy blank/null/nonblank destination, stable numeric ID/alias, missing/duplicate Enrollment, conflicting account/Enrollment name, duplicate certificate, archived Cohort, malformed/unknown row, dry-run/apply twice, changed source/schema/plan, and rollback/forward compatibility.
  3. Trigger equality on replay; uniqueness across versions/transitions; minimal-field canary; rollback/no-trigger; and proof that no request/model/transaction callback calls Relay, Datamailer, SES, or any network endpoint.
  4. Graduate zero/one/many/boundary, hidden/masked/full-PII roles, cross-Cohort denial, 0/1/limit/over-limit CSV, formula/control/Unicode canaries, worker crash/replay, expiry/deletion hook, and redacted audit.
  5. Wrapped supported legacy/current shapes, malformed/missing Course/Cohort reference, invisible year, no-activity learner, retained recalculation exact replay, checksum mismatch, and no certificate/email side effect.
  6. Studio/admin API/compatibility service parity for allowed/denied/missing/stale/replayed/bulk/partial states, private/no-store/noindex headers, CSRF/Bearer separation, rate/body bounds, audit/log redaction, and OpenAPI/registry coverage.
Independent browser evidence

Using synthetic .invalid fixtures at approximately 1440×900 and 390×844, the tester captures and reads the learner's active certificate link/history and unavailable/revoked state; operator certificate list/detail, issue/replace/revoke/reissue preview/confirmation/progress/result states; graduate export masked/denied/ready/expired states; and supported/malformed/no-activity historical Wrapped states. Include keyboard/focus/status, 200% zoom/320 CSS px reflow, reduced motion, and supported JavaScript-disabled confirmation/error behavior. Every image must show the intended page rather than a debug/404/broken layout and contain no real learner, certificate, recipient, credential, provider, or production data.

Explicit non-goals

  • No scoring algorithm, eligibility policy, Course/Cohort/Enrollment identity, authorization foundation, generic export infrastructure, privacy/retention policy, template body/version, delivery intent/job, preference rule, Relay/Datamailer/provider behavior, sender activation, or production migration choice.
  • No synchronous /certificates/notify, caller-managed notification loop, notify flag as receipt, request/on_commit network fanout, local renderer, temporary outbox, second delivery state, direct SES/Datamailer/Relay call, live send, or provider credential.
  • No new Wrapped generation, statistics redesign, destructive history cleanup, arbitrary URL inference, cross-cohort certificate inference, silent conflict repair, production/protected-data access, deployment, workflow dispatch, commit, merge, or push during PM/engineering/tester gates.

Lifecycle

After prerequisites integrate, one engineer leaves an uncommitted frozen candidate and complete versioned verification report. A separate tester recomputes the plan, verifies every criterion and screenshot, then PM performs product acceptance. Only after both gates pass may the engineer create a focused Closes #58 commit; the orchestrator locally merges with --no-ff, pushes, and on-call alone observes terminal CI. #233/#50 delivery enablement, #60 protected rehearsal, and any production/provider action remain later separately authorized work.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with _docs/PROCESS.md and specs 04, 06, 07, 09, and 10, then review prerequisite issues #57, #233, #245, and #60; implementation is explicitly dependency-blocked. Done requires synthetic-fixture migration and reconciliation evidence, preserved certificate and Wrapped history, and the stated transition, trigger, and export-safety contracts without production access or delivery activation.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, databases, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.