Effort: deep
accumulate_rui_costa.py:24 already cross-references SciDEX content
to a single human researcher (ORCID = "0000-0003-0495-8374") by
hard-coded ID, but there is no flow for an arbitrary researcher to
log in, prove they own that ORCID, and claim attribution on
SciDEX-generated artifacts where their name appears. ORCID's public
OAuth (3-legged flow) takes ~2 hours to wire and instantly elevates
SciDEX from "anonymous AI lab" to "credentialed research surface".
migrations/<date>_orcid_identity.sql:CREATE TABLE researcher_identity (
id UUID PRIMARY KEY,
orcid_id TEXT NOT NULL UNIQUE,
display_name TEXT,
email TEXT,
affiliations JSONB,
primary_works JSONB,
oauth_access_token TEXT,
oauth_refresh_token TEXT,
oauth_expires_at TIMESTAMP,
last_synced_at TIMESTAMP DEFAULT NOW(),
first_claimed_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE researcher_artifact_claims (
id UUID PRIMARY KEY,
researcher_id UUID NOT NULL REFERENCES
researcher_identity(id),
artifact_id TEXT NOT NULL REFERENCES artifacts(id),
claim_kind TEXT CHECK (claim_kind IN
('author','contributor','reviewer','endorser')),
evidence_url TEXT,
verified_at TIMESTAMP,
UNIQUE(researcher_id, artifact_id, claim_kind)
);api_routes/orcid_oauth.py:GET /oauth/orcid/login redirects to ORCID auth URL withstate=<random> stored in a short-TTL oauth_state table.GET /oauth/orcid/callback exchanges the code,/v3.0/{orcid}/record, populatesresearcher_identity. Sets a session cookie tied to the/researcher/claim: when logged increated_by isattributed_evidence; each row has Claim/Skip buttons.
primary_works (DOIs/PMIDs from ORCID) againstattributed_evidence JSONB; auto-createverified_at IS NULL claim rows for matches.
verified_at is set when an admin (or aq-impact-claim-
attribution (wave-2) consumes verified claims for the/researcher/{orcid_id} showsexternal_citations), and a public bio synthesisedtests/test_orcid_oauth.py: state cookieclient_id in the env, client_secret in the existing secretq-integ-github-bidirectional-sync introduces if it landsq-impact-claim-attribution (wave-2) — consumes verifiedaccumulate_rui_costa.py — pattern for ORCID DOI accumulation.q-integ-zotero-library-import — same OAuth callback shape.q-integ-bluesky-publish-pipeline — researcher can optorigin/main is at c0a454400; worktree clean-rebased before work.researcher_identity and researcher_artifact_claims tables did not existSELECT 1 FROM researcher_identity which raised relation not found.migrations/137_oauth_state_researcher_identity.py): createdoauth_state, researcher_identity, researcher_identity_history, andresearcher_artifact_claims tables; applied successfully.
api_routes/orcid_oauth.py): 3-legged ORCID OAuth with HMAC-signedoauth_state, access token exchange, ORCID record fetch, auto-claim,api.py via app.include_router.
/researcher/claim in api.py): lists candidate artifacts fromattributed_evidence DOI/PMID match; Claim button → POST /oauth/orcid/claims/{id}/{kind};/researcher/{orcid_id} in api.py): public page with_auto_claim_artifacts): scans primary_works DOIs/PMIDs againstattributed_evidence JSONB via ILIKE ANY; creates unverified author claims.primary_works is empty.
tests/test_orcid_oauth.py): 12 passing tests covering state signing,cb51c783c to branch orchestra/task/65a808b6-orcid-oauth-based-researcher-claim-flow.