You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The account adopted with the Course Management Platform is the single durable website account for learners and human staff. Evolve accounts.CustomUser and its existing relations in place; do not introduce a parallel website user, login, profile, session, or social-account identity.
A returning course learner or staff member must retain the same durable account and be able to authenticate through the preserved login methods after consolidation. The migration retains existing account primary keys where records are not duplicates and retains an explicit old-ID-to-survivor mapping for every reconciled duplicate. Existing enrollments, submissions, reviews, scores, certificates, account profile fields, preferences, social connections, staff links, and audit references continue to resolve to that account.
This decision does not make a GitHub-owned public Person profile an authentication record. Person remains the canonical public editorial identity from #3; an account may be explicitly linked to a Person without granting permissions or duplicating either record.
The consolidation starts from the adopted account schema and behavior, including:
accounts.CustomUser as AUTH_USER_MODEL, preserving the existing app/model/table and migration identity;
existing primary key, username/login identifier, normalized email evidence, password hash where supported, last_login, date_joined, active/staff/superuser state, and current account role fields;
certificate name, country/region, registration role, About me, GitHub/LinkedIn/personal website URLs, dark mode, preferred timezone, email preferences, and course-specific public-display preferences;
Django Allauth EmailAddress and SocialAccount/provider links and the currently supported learner login behavior;
Django session ownership and security semantics, protected-route next behavior, logout, and account settings;
account foreign keys from enrollments, submissions, reviews, votes, complaints, scores, certificates, tokens/principals, staff assignments, audit records, and other adopted course data.
The consolidated application must have one authoritative user lookup and one authenticated request.user. New public, learner, course, and Studio surfaces consume that identity and the same session rather than copying account fields into another website profile.
Scope
In-place account consolidation
Inventory the adopted user model, authentication backends/providers, Allauth email/social records, session backend/cookie behavior, login/logout/settings routes, user foreign keys, compatibility APIs, and account navigation before changing them.
Evolve the adopted model through expand-and-contract migrations. Do not replace it with a greenfield user table or renumber clean source accounts.
Make verified normalized email the learner account identity without losing legacy username/login compatibility during the migration window.
Preserve existing login provider connections and callback behavior. A provider callback for an existing person must resolve the existing account, never create a second website account merely because the visitor arrived from a new site surface.
Keep current authenticated sessions valid through an ordinary in-place release. Consolidation must not perform a blanket session flush. Legacy-host compatibility sessions remain valid while those routes are served.
Produce and test a secure cross-host continuity plan for the eventual courses.datatalks.club → datatalks.club transition. It may not broaden cookie scope without security approval or expose handoff credentials in URLs, logs, analytics, screenshots, or referrers. Replay, expiry, CSRF/fixation, logout, revocation, and next handling must be explicit. If browser cookie boundaries require visible reauthentication, it must be a reviewed migration exception: the login returns to the intended route and resolves the same account ID, never a new account.
Duplicate and conflict reconciliation
A snapshot-pinned, idempotent dry run groups candidate duplicates using normalized email, Allauth verified-email evidence, provider plus provider UID, username/login identifiers, and account relations. It reports exact source IDs and counts without logging raw provider payloads, tokens, or unrelated PII.
Generate a reviewed source-account → survivor mapping. Existing distinct accounts are never merged solely because a social provider supplied an unverified email or because one record has the newest last_login.
Ambiguous ownership, conflicting provider UIDs, or irreconcilable identifiers fail closed into a quarantine/review report. They do not create an account, attach a provider, or select the “most recent” user automatically.
Preserve the selected survivor's primary key. Every absorbed source ID remains recorded in a durable alias/migration map so imports, audit evidence, compatibility APIs, and rollback can resolve it.
Reparent all account-owned relationships exactly once and reconcile totals before an absorbed record can be disabled. Course work from multiple legitimate source records is retained and cannot cross-link to another person's account.
Privilege is never gained by union: is_staff, superuser status, groups, course/cohort capabilities, API credentials, and public Person links require their existing authoritative evidence or separate reviewed grant. Duplicate reconciliation cannot turn a learner into staff.
Consent is never inferred or widened. Profile/preference conflicts use documented per-field rules, choose the privacy-preserving state where applicable, and surface unexplained differences instead of silently overwriting them.
Social connections move only with verified ownership evidence. Password hashes and recovery/email-change state retain safe Django semantics; affected tokens/sessions are invalidated only when required by a documented security condition.
Signed-out navigation exposes one same-host login destination. Signed-in navigation exposes the signed-in account, account settings, learner/course destinations, and one logout action. Staff-only destinations are shown through the shared capability policy, not by introducing another staff login or public-Person inference.
Login, logout, settings, social-connection, callback, and protected-route links preserve safe next behavior and never bounce to a second account system or legacy login page.
Desktop and mobile navigation are keyboard complete, expose state accessibly, avoid horizontal overflow, and do not publicly cache authenticated content.
Anonymized review/test content
Content-only local imports and the P0: Build a fully navigable Django review skeleton #97 representative release create no real CustomUser, EmailAddress, SocialAccount, session, provider credential, enrollment, or staff record.
Deterministic local/browser fixtures use synthetic non-routable identities and contain no production email, account ID, social UID, access token, registration data, or profile PII.
GitHub Person projections used by content review remain public content records and do not silently create/link authenticated accounts.
Compatibility, API, and observability
Existing course HTML and authenticated API behavior keeps using the adopted account through the compatibility window. New learner and management APIs reference the same durable account identity; no endpoint gets a website-only user table.
Authentication, linking, duplicate review, merge, disablement, session handoff, and rollback outcomes emit redacted audit events. Raw OAuth/social payloads, email addresses, password material, session keys, cookies, tokens, and handoff credentials never enter logs or issue/test artifacts.
Metrics distinguish successful returning login, explicit reauthentication, account-link conflict, unexpected new-account creation, session/auth failure, and rollback without using PII labels.
Non-goals
No second website account/profile/login service, separate learner and staff user tables, public Person as authentication identity, or account copy generated from content records.
No generic automatic merge by newest login, unverified email, fuzzy name, or social display name.
No destructive deletion of duplicate rows, removal of compatibility identifiers, blanket logout, cookie-security weakening, production PII in local fixtures, or secret/provider payload logging.
Pin the source snapshot and run a no-write inventory covering every account, email/social/login relation, session/auth contract, dependent table, and duplicate/conflict cluster.
Add nullable mapping/alias/reconciliation state and any normalized identity constraints without dropping old columns, identifiers, or rows.
Dry-run the reviewed mapping repeatedly; report source/target counts, unchanged IDs, duplicate groups, ambiguous groups, per-table relationship totals/checksums, field decisions, and rejected rows.
Apply survivor mappings and dependent-row changes idempotently with outbound email and provider side effects disabled. Keep absorbed account rows and compatibility identifiers available but unable to authenticate twice after verified consolidation.
Validate login, provider linking, sessions, account settings, staff capability lookup, course workflows, APIs, and audit references on a production-like copy before enabling the new resolver/navigation in production.
Cut over behind an observable feature/configuration boundary, run returning-account and duplicate canaries, and retain the old resolver plus non-destructive mapping data for the rollback window.
Rollback uses an immutable application revision that can still read the expanded schema. It never reverses successful relationship moves destructively, re-enables two independently authenticating accounts for one person, discards post-cutover writes, or clears all sessions. Workers/provider callbacks are paused or pinned as needed, alias mappings remain available, and any authentication-failure threshold is a release rollback trigger under spec 09.
Acceptance criteria
accounts.CustomUser remains the one AUTH_USER_MODEL; all site, course, learner, Studio, compatibility, and API authentication resolves one durable account with no parallel website user/profile/session model.
The account inventory classifies every identity field, login/provider route, session behavior, dependent foreign key, compatibility identifier, and current account navigation action before migration.
Clean source accounts retain their primary IDs and preserved fields/relations; every reconciled source ID has a durable reviewed survivor mapping and no account-owned educational or audit record is lost or attached to the wrong person.
Verified normalized-email identity and legacy login compatibility are introduced through forward/backward-tested expand-and-contract migrations without renumbering or recreating the adopted user table.
Duplicate email, case variant, username/email overlap, multiple EmailAddress, conflicting social UID, staff/learner collision, and incomplete-profile cases produce deterministic reviewed results; ambiguous cases fail closed and newest-last_login is not an automatic authority.
Existing supported learner login methods, provider connections, callback outcomes, account settings/profile/social links/preferences, logout, safe next, and recovery/change behavior remain characterized and pass for returning accounts.
An ordinary consolidation release preserves valid existing sessions and does not globally log users out. Legacy-host and eventual cross-host continuity/explicit-reauth behavior passes the approved fixation, replay, expiry, revocation, logout, cache, and credential-leak tests while always returning the same account ID.
Duplicate reconciliation cannot grant staff/superuser/group/course capability, infer consent, expose another user's data, or create a new social link without verified ownership.
Every new website surface renders the one shared signed-out login or signed-in account navigation at desktop/mobile; protected actions use the same session and account, and staff links use capability policy rather than a second login.
Existing course learner and authenticated API characterization suites pass against the consolidated account; compatibility and new APIs agree on account identity and denial behavior.
The migration is snapshot-pinned, dry-runnable, idempotent, redacted, count/checksum reconciled, and safe to rerun; outbound email/provider side effects remain disabled during rehearsal/import.
Production-like rehearsal and rollback prove account/login/session continuity, alias resolution, no unexplained relationship-count difference, no lost post-cutover write, no double-authenticating duplicate, and no destructive reversal.
Logs, metrics, audit events, migration reports, screenshots, and issue comments contain no raw OAuth payload, production email/profile PII, password/session/token/cookie/handoff secret, or provider credential.
Django and integration scenarios
Single identity: authenticate through each currently supported login path, visit public content, account settings, course dashboard, and an authorized Studio route, and assert one request.user.pk, session owner, and account record throughout.
Returning learner: import a source learner with profile/preferences, social connection, enrollment, submission, review, score, and certificate; log in after consolidation and verify the same account ID and all records/fields.
Duplicate matrix: exercise case/whitespace email variants, username-email overlap, one/multiple verified emails, conflicting provider UID, two active learners with course history, learner/staff collision, and missing email. Verify reviewed survivor/alias or fail-closed quarantine with no privilege/consent union.
Social linking: returning Google/GitHub/Slack fixtures connect only after verified ownership, replay idempotently, and reject unverified/conflicting claims without selecting the most recent user or logging provider payloads.
Session lifecycle: preserve a valid pre-release session through the in-place migration; test fixation, idle/absolute expiry, logout, staff disablement, password/email/security changes, legacy-host compatibility, and cross-host handoff or explicit reauthentication without account duplication.
Migration: run dry-run twice, apply twice, and reconcile account IDs, aliases, every dependent table, totals/checksums, ambiguity report, disabled duplicate behavior, and no outbound side effects. Run forward/backward migration-window tests against a production-like database copy.
Rollback: create account-owned writes after cutover, roll the application back without reversing data, and prove both old/new identifiers resolve safely, sessions follow policy, new writes remain, and only one account authenticates.
API parity: exercise representative session-authenticated and compatibility token/principal requests before/after migration; assert same account, schema/status/authorization behavior, safe 401/403, and no cross-account lookup leak.
Anonymized review: load P0: Build a fully navigable Django review skeleton #97 content fixtures and assert zero new user, email/social, session, enrollment, registration, token, and staff rows plus a PII/secret artifact scan.
Browser scenarios
Run at desktop and mobile widths with deterministic synthetic accounts:
Start signed out on /, use the shared Login control, authenticate as an existing learner, return via next, and navigate articles/events/courses/account settings without another login or account ID change.
From the same signed-in shell, traverse course dashboard/homework/project/profile surfaces; verify account navigation, profile/social-link values, theme/timezone preferences, logout, browser-back privacy, and no legacy/second login dead end.
Exercise a deterministic social-provider callback for an existing account and a duplicate/conflict denial. The successful path lands on the same account; the conflict gives safe recovery guidance without exposing whether another account exists.
Use a staff account through the shared shell; authorized Studio navigation is present, learner navigation still uses the same account, and a learner/disabled staff receives the expected denial without a second staff identity.
Exercise the approved legacy-host-to-canonical continuity or explicit-reauth flow, including intended destination, back/refresh/replay/logout, and verify no token appears in URL/history/screenshot.
Load the P0: Build a fully navigable Django review skeleton #97 content-only review dataset signed out and signed in; shared navigation changes appropriately while database counts prove the content import itself created no identity or PII.
Capture and inspect representative desktop/mobile login, account-menu/settings, returning learner, staff allowed/denied, duplicate-conflict, and continuity/reauth states under .tmp/, with synthetic identifiers only.
P0: Build a fully navigable Django review skeleton #97 is independent for content rendering but must use the shared account-aware shell and must keep its content projections anonymized. It does not need account migration or real identity data to complete its read-only review slice.
Delivery convention
Follow _docs/PROCESS.md. Engineering implements and tests without committing; an independent tester verifies migration/browser evidence and synthetic screenshots; product acceptance follows. No pull request is created.
Parent epics: #5, #7, #10
Related implementation owners: #52, #54, #60, #61, and #97
Owner decision and outcome
The account adopted with the Course Management Platform is the single durable website account for learners and human staff. Evolve
accounts.CustomUserand its existing relations in place; do not introduce a parallel website user, login, profile, session, or social-account identity.A returning course learner or staff member must retain the same durable account and be able to authenticate through the preserved login methods after consolidation. The migration retains existing account primary keys where records are not duplicates and retains an explicit old-ID-to-survivor mapping for every reconciled duplicate. Existing enrollments, submissions, reviews, scores, certificates, account profile fields, preferences, social connections, staff links, and audit references continue to resolve to that account.
This decision does not make a GitHub-owned public
Personprofile an authentication record.Personremains the canonical public editorial identity from #3; an account may be explicitly linked to a Person without granting permissions or duplicating either record.Product and architecture authority
accountsapp and custom user model98a235283904b4ef9ad29e196298540756cf1bcc.Preserved identity contract
The consolidation starts from the adopted account schema and behavior, including:
accounts.CustomUserasAUTH_USER_MODEL, preserving the existing app/model/table and migration identity;last_login,date_joined, active/staff/superuser state, and current account role fields;EmailAddressandSocialAccount/provider links and the currently supported learner login behavior;nextbehavior, logout, and account settings;The consolidated application must have one authoritative user lookup and one authenticated
request.user. New public, learner, course, and Studio surfaces consume that identity and the same session rather than copying account fields into another website profile.Scope
In-place account consolidation
courses.datatalks.club→datatalks.clubtransition. It may not broaden cookie scope without security approval or expose handoff credentials in URLs, logs, analytics, screenshots, or referrers. Replay, expiry, CSRF/fixation, logout, revocation, andnexthandling must be explicit. If browser cookie boundaries require visible reauthentication, it must be a reviewed migration exception: the login returns to the intended route and resolves the same account ID, never a new account.Duplicate and conflict reconciliation
last_login.is_staff, superuser status, groups, course/cohort capabilities, API credentials, and public Person links require their existing authoritative evidence or separate reviewed grant. Duplicate reconciliation cannot turn a learner into staff.Shared website account navigation
nextbehavior and never bounce to a second account system or legacy login page.Anonymized review/test content
CustomUser,EmailAddress,SocialAccount, session, provider credential, enrollment, or staff record.Personprojections used by content review remain public content records and do not silently create/link authenticated accounts.Compatibility, API, and observability
Non-goals
Personas authentication identity, or account copy generated from content records.Migration and rollback contract
Rollback uses an immutable application revision that can still read the expanded schema. It never reverses successful relationship moves destructively, re-enables two independently authenticating accounts for one person, discards post-cutover writes, or clears all sessions. Workers/provider callbacks are paused or pinned as needed, alias mappings remain available, and any authentication-failure threshold is a release rollback trigger under spec 09.
Acceptance criteria
accounts.CustomUserremains the oneAUTH_USER_MODEL; all site, course, learner, Studio, compatibility, and API authentication resolves one durable account with no parallel website user/profile/session model.EmailAddress, conflicting social UID, staff/learner collision, and incomplete-profile cases produce deterministic reviewed results; ambiguous cases fail closed and newest-last_loginis not an automatic authority.next, and recovery/change behavior remain characterized and pass for returning accounts.Django and integration scenarios
request.user.pk, session owner, and account record throughout.Browser scenarios
Run at desktop and mobile widths with deterministic synthetic accounts:
/, use the shared Login control, authenticate as an existing learner, return vianext, and navigate articles/events/courses/account settings without another login or account ID change.Capture and inspect representative desktop/mobile login, account-menu/settings, returning learner, staff allowed/denied, duplicate-conflict, and continuity/reauth states under
.tmp/, with synthetic identifiers only.Dependencies and coordination
Delivery convention
Follow
_docs/PROCESS.md. Engineering implements and tests without committing; an independent tester verifies migration/browser evidence and synthetic screenshots; product acceptance follows. No pull request is created.