DataTalksClub / DataTalksClub/website
Preserve graduates, certificates, and historical Wrapped records
Nobody has claimed this yet.
- 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
_docs/PROCESS.md04 — Enrollment and certificates, preserved learner behavior, management, migration, and compatibility05 — Course/cohort communication and Relay ownership06 — Studio/admin API parity, idempotency, operations, audit, and safety07 — learner/certificate privacy, high-risk controls, retention, and export safety09 — course migration, reconciliation, and rollback10 — Course/Cohort verification- resolved owner decisions #23 and #28, including the accepted permanence of an already-issued real certificate name after a later erasure request
- accepted copied-source characterization/provenance baseline #30
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.
versionis a positive, monotonically increasing certificate lifecycle sequence and is distinct from the mutable-resource concurrencyrevision. 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
- Issue: absent → issued version 1 after the accepted #57 eligibility result. This is the only first issue.
- Replace/reissue while issued: issued → issued at
version + 1when 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. - 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. - Reissue after revocation: revoked → issued at
version + 1with 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, orreissued); - 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:
- #52 — exact Course/Cohort staff assignment plus function/object/field authorization and high-risk audit boundary;
- #53 — final Cohort lifecycle, canonical/legacy route and alias identity, archive protection, and duplication exclusions;
- #244 — final cohort-isolated Enrollment identity, preserved certificate/display/history fields, learner dashboard/history access, and migration mapping;
- #245 — accepted masked Enrollment query, certificate-state field, PII/export capability and bounded artifact/operation interface; and
- #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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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,notifyflag as receipt, request/on_commitnetwork 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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