SPB Git

spb/wp44_uqo

Public
40.9 KB · 614 lines latex
1% ============================================================================2% Appendix B — Search strategies, sources and acquisition record3% Written by Agent L (Appendices B/C instance), 2026-11-03. Operational record4% of what ingestion/*.py, systematic_review/define_corpus.py and the runbook5% actually did; Part III gives the rationale and must not be repeated here.6% Corpus numbers only through macros of tables/numbers.tex; sizes, hashes,7% dates, file counts and parameter values are provenance identifiers and are8% typed from data/raw/README.md, reports/PHASE0_FEASIBILITY.md and the ledgers.9% ============================================================================1011\section{Search strategies, sources and acquisition record}12\label{sec:appB}1314This appendix is the operational counterpart of Part~III\@. Part~III explains15why four bibliographic sources were combined and what each contributes; the16present appendix records, source by source, the endpoint or bucket that was17read, the release or version that was read, the filters that were applied,18the rate limits and budgets under which the reading took place, the licence19of the material, the integrity checks performed on arrival, and what was20sought but not obtained. It then gives the reporting items of the PRISMA-S21extension for literature searches \citep{rethlefsen2021prismas} in the form22they take for a scope-filtered mapping study, the exclusion codes of the23eligibility stage with their predicates, the type and platform lists on which24those predicates depend, and the record-linkage cascade with its thresholds.25Every statement below can be checked against the scripts in26\texttt{ingestion/} and \texttt{systematic\_review/}, the runbook27(\texttt{reproducibility/RUNBOOK.md}) and the provenance note28\texttt{data/raw/README.md}; where a count is a property of the corpus it is29rendered by a macro of \texttt{tables/numbers.tex}, and where no macro exists30the value is marked pending rather than typed.3132\subsection{Acquisition at a glance}33\label{sec:appB-glance}3435Table~\ref{tab:appB-acq} lists the five acquisitions in the order of the36runbook. All were performed from the compute node \texttt{M3U96b} between the37evening of 2~November and 3~November~2026 (Eastern Time), with no credential38of any kind: the OpenAlex key, the Semantic Scholar key and the Kaggle39credentials were tested as absent before the first download40(\texttt{reports/PHASE0\_FEASIBILITY.md}, §2). The absence of credentials is41not incidental. It determined the choice of the snapshot over the API for42OpenAlex (decision D1 of the plan), it excluded Semantic Scholar (D3), and it43capped the post-snapshot increment at the public daily budget44(Section~\ref{sec:appB-increment}).4546\begin{table}[htbp]47\centering\footnotesize48\caption{Acquisitions performed for the WP44 corpus, in runbook order.}49\label{tab:appB-acq}50\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{1.8cm}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}p{2.2cm}>{\raggedright\arraybackslash}p{4.0cm}}51\toprule52Source & Object read & Obtained & Integrity record \\53\midrule54OpenAlex works & public snapshot, release 2026-10-14, \texttt{data/jsonl/works/}, 1{,}898 part files, 675{,}298{,}563{,}638 bytes gzip & 2026-11-02 21:19 to 23:05 ET, HTTPS, 8 parallel streams & manifest copy; content length and SHA-256 per file in \texttt{works/ledger.sqlite}; 1{,}898/1{,}898 done, 0 malformed lines; record total equal to the manifest total \\55OpenAlex entities & snapshots of topics, subfields, fields, domains, sources, institutions, publishers, funders, concepts & 2026-11-02 & \texttt{entities\_ledger.json}: content length and SHA-256 per file \\56OpenAlex API & works with any CS topic and publication date 2026-10-01 to 2026-11-02, by day and by primary field & 2026-11-03 onward, within the public budget & \texttt{api\_increment/}\allowbreak\texttt{ledger.json}: calls, cost, records and pages per slice; raw pages kept verbatim \\57DBLP & \texttt{dblp.xml.gz} dated 2026-11-02 22:39 UTC, 1{,}116{,}270{,}104 bytes, with \texttt{dblp.dtd} and \texttt{dblp.xml.gz.md5} & 2026-11-02 & published MD5 \texttt{ff1c424c3b4cef36}\allowbreak\texttt{582f647c36df27c0} verified on the whole file before parsing; parse report with size, MD5 of the first GiB, counts by type and year \\58arXiv & OAI-PMH, set \texttt{cs}, metadata prefix \texttt{arXiv}, datestamps 2018-01-01 to 2026-11-02 & 2026-11-02 to 03 & \texttt{harvest\_state.json} (pages, records, seconds per window); 758 raw XML pages kept; \texttt{arxiv\_parse\_}\allowbreak\texttt{report.json} \\59ACL Anthology & \texttt{anthology+abstracts.bib.gz}, 46{,}723{,}382 bytes & 2026-11-02 & SHA-256 \texttt{44c3f7e907f221b740}\allowbreak\texttt{bea09069818a3d7c0d}\allowbreak\texttt{fd88b2da3b750d9f02}\allowbreak\texttt{955efc58b3}; \texttt{acl\_parse\_}\allowbreak\texttt{report.json} \\60\bottomrule61\end{tabularx}62\end{table}6364\subsection{OpenAlex works snapshot}65\label{sec:appB-openalex}6667\paragraph{Bucket, release and manifest.}68The works entity of the OpenAlex snapshot \citep{priem2022openalex} is69distributed as gzip-compressed JSON Lines files in the public S3 bucket70\texttt{s3://openalex/} under the prefix \texttt{data/jsonl/works/}, partitioned by the date at which the71records were last updated (\texttt{updated\_date=YYYY-MM-DD/}\allowbreak\texttt{part\_NNNN.gz}).72The release read is the one whose \texttt{manifest.json} carries the date732026-10-14; the manifest lists 1{,}898 part files with, for each, its URL,74its content length in bytes and its record count, together with the totals75(\LinkSlimTotal{} works, 675{,}298{,}563{,}638 bytes). A copy of the manifest76is kept as \texttt{data/raw/openalex/works\_manifest\_2026-10-14.json} and is77the reference against which the ledger is checked. No AWS client was78installed on the node; the bucket is public and each object was fetched over79HTTPS from \url{https://openalex.s3.amazonaws.com/} with \texttt{curl}80(\texttt{-L --retry 5 --retry-delay 3 --retry-all-errors -C -}, the last81option resuming an interrupted transfer), up to five outer attempts per file82with a linearly increasing pause, and the transfer was accepted only when the83byte count on disk equalled the content length in the manifest. The measured84single-stream throughput from the node was about 71~MB/s and the aggregate85over eight streams about 110~MB/s, which fixed the worker count at eight and86the wall time at one hour and forty-six minutes for the whole bucket87(\texttt{ingestion/openalex\_snapshot.py --workers 8}).8889\paragraph{Filter applied on the stream (pass 1).}90Each part file was decompressed in memory and parsed line by line91(\texttt{orjson}); a line that failed to parse was counted as malformed and92skipped, and the count of such lines (zero for every file) is in the ledger.93For every parsed work $w$ the script evaluated the predicate94\begin{equation}95  \mathrm{CS}(w) \;=\; \Bigl[\exists\, \theta \in \mathrm{topics}(w) :96  \mathrm{field}(\theta) = \text{\texttt{fields/17}}\Bigr]97  \;\vee\; \Bigl[\mathrm{field}\bigl(\mathrm{primary\_topic}(w)\bigr) = \text{\texttt{fields/17}}\Bigr],98  \label{eq:appB-csfilter}99\end{equation}100that is, whether any of the up to three topics attached to the record, or its101primary topic, belongs to the OpenAlex field with identifier~17102(\emph{Computer Science}; \citealp{openalex2024topics}), and kept the103\emph{complete} JSON record when $\mathrm{CS}(w)$ held and104\texttt{publication\_year} lay in $[2018, 2026]$. Records flagged105\texttt{is\_xpac} (OpenAlex's expansion of coverage from non-Crossref106sources) were kept at this stage with their flag, so that the exclusion could107be applied later by the eligibility predicate rather than silently at108ingestion. The retained records were re-compressed with Zstandard (level~6)109into one file per part, \texttt{works/cs/}\allowbreak\texttt{updated\_date=X\_\_part\_NNNN.jsonl.zst},110about 20~GB in total for111\PrismaIdentifiedOpenalexCsTopicTwoZeroOneEightTwoZeroTwoSixPassOne{}112records. No text search of any kind was performed: the scope of the OpenAlex113layer is defined entirely by the topic classifier's field assignment, which is114the search strategy appropriate to a field-level mapping and whose115consequences are discussed in Part~III\@.116117\paragraph{Slim table of every work.}118In the same pass the script wrote, for \emph{every} work in the snapshot119regardless of field or year, one row of a slim Parquet table with the120following columns: OpenAlex identifier (as an integer), DOI (lower-cased,121prefix removed), publication year and date, type, \texttt{cited\_by\_count},122primary topic, subfield, field and domain identifiers, the value of123$\mathrm{CS}(w)$, the XPAC, open-access and retraction flags, language,124primary source identifier, number of referenced works, number of authors,125whether an abstract is present, and FWCI\@. The 1{,}898 per-part files were126later concatenated into \texttt{compact/slim\_all\_works.parquet}127(\LinkSlimTotal{} rows). The slim table is what allows a reference from a128computer-science work to a work in another field to be typed and dated129(Part~IV), and it is also what made the second pass of130Section~\ref{sec:appB-pass2} possible without re-reading the bucket for a131search.132133\paragraph{Integrity and provenance.}134Before parsing, the SHA-256 digest of the gzip bytes of each part file was135computed and written, with the file's URL, content length, manifest record136count, parsed record count, retained record count, malformed-line count,137download and total times, attempt count and timestamps, to a SQLite ledger138(\texttt{works/ledger.sqlite}, table \texttt{files}); a second table139(\texttt{runs}) records the manifest date and totals, the worker count, the140host name and the command line of every invocation. The ledger makes the job141resumable (a file with status \texttt{done} is never re-read) and is the142provenance record of the raw layer. The raw \texttt{.gz} files were deleted143after a successful parse (decision D6 of the plan): the snapshot is a public,144versioned release, the manifest and the per-file digests permit an145independent party to re-download and verify any part, and keeping 675~GB of146compressed JSON on the node would have precluded the rest of the pipeline.147Three checks were made at the end of the pass and are recorded in148\texttt{data/raw/README.md}: all 1{,}898 files have status \texttt{done}; the149sum of parsed records over files equals the manifest total; and the sum of150malformed lines is zero. The OpenAlex snapshot is released under CC0~1.0151(\texttt{LICENSE.txt} in the bucket, copied next to the manifest), which152permits redistribution of derived record-level data.153154\paragraph{Entity snapshots.}155The smaller entity snapshots were downloaded from the same bucket by156\texttt{ingestion/}\allowbreak\texttt{openalex\_entities.py}, using each157entity's own manifest, with the same length check and SHA-256 ledger158(\texttt{entities\_ledger.json}). The manifests list 4{,}516 topics,159252 subfields, 26 fields, 4 domains, 268{,}629 sources, 143{,}455 institutions,16010{,}713 publishers, 41{,}799 funders and 65{,}026 concepts; the topics161snapshot was flattened to \texttt{topics\_flat.csv} (identifier, name,162subfield, field, domain, works count, keywords, description) and is the163vocabulary over which the ontology of Appendix~A maps OpenAlex topics to164categories. The authors entity was not downloaded, because the authorship165blocks embedded in each work record already carry the author identifier,166display name, ORCID, position and institutions; the keywords entity167(1{,}881{,}723 entries in its manifest) was not downloaded either, the168per-work keyword lists being embedded as well.169170\subsection{Second snapshot pass by identifier set}171\label{sec:appB-pass2}172173The first pass retains what OpenAlex calls computer science. The record174linkage of Section~\ref{sec:appB-linkage} showed that175\LinkDblpUnlinkedWithDoiInSlim{} DBLP records of 2018--2026 have a DOI that176exists in the slim table of all works but not in the retained subset, that is,177works indexed by DBLP that OpenAlex labels with no computer-science topic.178Rather than accept OpenAlex's boundary, the pipeline reads the bucket a179second time in \emph{identifier-set mode}. The linkage script writes180the list \texttt{data/metadata/}\allowbreak\texttt{needed\_oa\_ids.csv} of OpenAlex identifiers to181recover (\LinkNeededOaIdsSecondPassDblpDoi{} reached through a DBLP DOI and182\LinkNeededOaIdsSecondPassArxivDoi{} through an arXiv DOI), and183\texttt{openalex\_snapshot.py --id-list} streams every part file again,184keeping exactly the records whose identifier is in the set and writing no slim185rows. The identifier set is loaded in each worker process (macOS uses the186\texttt{spawn} start method, so module globals are rebuilt per worker), the187same download, length and SHA-256 discipline applies, and the output lands in188\texttt{works\_pass2/cs/} with its own ledger. The recovered records are189normalised with the suffix \texttt{\_p2} and the linkage is rerun on the190union of both passes (\texttt{ingestion/run\_pass2\_chain.sh}). The second191pass costs the same bandwidth as the first---about one hour and fifty minutes192of streaming---because an identifier lookup cannot be pushed to the bucket;193the price was accepted because the alternative, fetching the identifiers one194by one through the API, would have exceeded the public budget by two orders of195magnitude. The PRISMA counts of Part~III were generated from the first pass196(\PrismaIdentifiedOpenalexTotalRecordsLoaded{} records loaded); the second197pass adds its records to $\mathcal{W}_0$ when it completes and the counts are198regenerated by the same scripts, so the identification total in the final199PRISMA figure is \pending{PrismaIdentifiedOpenalexTotalRecordsLoaded after the second pass}200rather than the first-pass value quoted here.201202\subsection{OpenAlex API increment}203\label{sec:appB-increment}204205The snapshot ends on 14~October 2026 and the observation window on2062~November 2026. The gap was covered by the REST API207(\texttt{https://api.openalex.org/works}) without a key. Two constraints of the208unkeyed API shaped the harvest and are recorded here because they bound what209the increment can contain. First, cursor paging requires a key, so the210harvest used basic page numbering, which the API caps at 10{,}000 results per211filter expression; the harvest is therefore \emph{sliced}, one request series212per publication day $d$ and per primary-field class, with the filter213\begin{quote}\ttfamily214topics.field.id:17, from\_publication\_date:$d$, to\_publication\_date:$d$,215primary\_topic.field.id:17 \quad (slice ``cs\_primary'')\\216topics.field.id:17, from\_publication\_date:$d$, to\_publication\_date:$d$,217primary\_topic.field.id:!17 \quad (slice ``cs\_secondary'')218\end{quote}219for every day from 2026-10-01 to 2026-11-02 (the start was placed two weeks220before the snapshot date to absorb records that OpenAlex back-dates), 200221results per page, a \texttt{select} list of the fields used by the222normaliser (identifiers, dates, type, language, locations, open access,223authorships, topics, keywords, referenced works, citation counts, abstract224inverted index, flags, FWCI, funders, indexed-in, full-text flag, APC), the225polite-pool \texttt{mailto} parameter and a descriptive user agent. A slice226whose reported \texttt{count} exceeds 10{,}000 is marked \texttt{truncated} in227the ledger; the two-way split by primary field was chosen because a single228day of computer-science output in 2026 can exceed the cap while each half229rarely does. Second, the unkeyed API is metered: list-and-filter calls are230charged at about \$0.0001 each against a shared budget of \$0.10 per day,231and the script stops at \$0.085 per day (a margin for the free single-record232lookups used for reference verification), sleeps 0.12~s between calls, and233halts on HTTP~429, 403 or any 5xx response so that the next day's run resumes234from the ledger. Every page is stored verbatim as235\texttt{api\_increment/pages/<day>\_<slice>/page\_NNNN.json}; the ledger236(\texttt{api\_increment/ledger.json}) records per slice the pages, records,237reported count, completion and truncation flags, and globally the number of238calls and the cost. At the time of writing the harvest had used the budget239of several consecutive days and was not complete; its totals240(\pending{IncrementApiCalls}, \pending{IncrementRecordsRetrieved},241\pending{IncrementTruncatedSlices}) will be reported from the ledger when the242last slice closes. The conversion step (\texttt{ingest\_api\_increment.py})243rewrites the pages into the same \texttt{.jsonl.zst} part format as the244snapshot filter (\texttt{works\_pass3/}), skipping any work whose identifier245is already in the pass-1 or pass-2 tables and writing an246\texttt{increment\_report.json} (pages, new works, works already present); the247number of works added by the increment is \pending{IncrementNewWorks}. Two248limitations follow from the design and are not recoverable from the ledger: a249truncated slice loses the records beyond the 10{,}000th in the API's default250ordering, and a record created after 2~November 2026 with a publication date251inside the window is absent whatever its date. Both affect only the last252weeks of a window that is already treated as right-censored (Part~III), so no253analysis depends on them.254255\subsection{DBLP}256\label{sec:appB-dblp}257258\paragraph{Dump and version.}259DBLP \citep{ley2002dblp, ley2009dblp} publishes its whole database as a260single XML file. The file read is \texttt{dblp.xml.gz} as served on2612~November 2026 (file timestamp 22:39 UTC), 1{,}116{,}270{,}104 bytes, together262with the document type definition \texttt{dblp.dtd} and the published263checksum file \texttt{dblp.xml.gz.md5}. The MD5 of the whole file,264\texttt{ff1c424c3b4cef36582f647c36df27c0}, was computed and compared with the265published value before parsing; the parse report additionally stores the file266size and an MD5 of the first gibibyte as a quick identity check for later267sessions. DBLP is released under the Open Data Commons Attribution licence268(ODC-BY~1.0), which permits the redistribution of derived data with269attribution. The download from \texttt{dblp.org} proceeded at about 12~MB/s,270the slowest link of the whole acquisition.271272\paragraph{Parsing rule.}273The DTD matters: DBLP titles use XML entities for accented characters, and a274parser that does not resolve them against \texttt{dblp.dtd} corrupts titles275and therefore the title-based linkage. The parser276(\texttt{ingestion/dblp\_parse.py}) is \texttt{lxml.etree.iterparse} over the277gzip stream with \texttt{load\_dtd=True}, \texttt{resolve\_entities=True} and278\texttt{huge\_tree=True}, run from the directory containing the DTD because279the dump references it relatively. It emits one row per record of the types280\textit{article}, \textit{inproceedings}, \textit{proceedings}, \textit{book},281\textit{incollection}, \textit{phdthesis}, \textit{mastersthesis} and282\textit{data}; person records (\texttt{www}) are consumed and discarded. For283each record it keeps the key, type, modification date, publication-type284attribute, title (concatenated text of the title element, so that embedded285markup such as subscripts is flattened), year, author names and DBLP person286identifiers, editors, journal, booktitle, volume, number, pages, publisher,287series, the DOI (taken from the first electronic-edition URL beginning with288\texttt{https://doi.org/}, lower-cased), the full list of electronic-edition289URLs, the URL, the cross-reference to the proceedings record, and the290\emph{stream}, the part of the key before its last slash291(\texttt{journals/tocs}, \texttt{conf/nips}), which is DBLP's venue identifier292and the unit on which the ontology's venue signals are defined. All years are293parsed; counts by type and by year for the entire archive are written to294\texttt{dblp\_parse\_report.json}, and the records of 2018--2026295(\LinkDblpTwoZeroOneEightTwoZeroTwoSix{} after the type restriction of the296linkage step) are the ones that enter the corpus. The total number of297publication records in the dump is \pending{DblpParsedRecords}. A second298script (\texttt{dblp\_streams.py}) lists the streams with at least one hundred299records of type article or inproceedings in 2018--2026, with their first and300last years and a sample journal or booktitle name; this list301(\texttt{taxonomy/dblp\_streams\_2018\_2026.csv}) is the input of the302venue-to-category mapping and is not itself a corpus filter.303304\subsection{arXiv}305\label{sec:appB-arxiv}306307\paragraph{Protocol and windows.}308arXiv \citep{ginsparg2011arxiv} metadata were harvested through its OAI-PMH309endpoint \texttt{https://oaipmh.arxiv.org/oai} with the verb310\texttt{ListRecords}, the set \texttt{cs} and the metadata prefix311\texttt{arXiv}, which returns arXiv's own schema (identifier, dates, title,312abstract, authors with forenames and keynames, categories, comments, journal313reference, DOI, licence, report number, MSC and ACM classes). The harvest was314sliced into calendar-month windows on the OAI \texttt{from}/\texttt{until}315parameters from 2018-01-01 to 2026-11-02; each window was walked with the316resumption token until exhausted, and every response page was written317verbatim to \texttt{raw/<window>/page\_NNNN.xml} before being parsed, so that318the Parquet table can be rebuilt from the raw pages319(\texttt{--parse-only}) without touching the network. The harvester waited3203.5~s between requests, honoured the \texttt{Retry-After} header on HTTP~503321(arXiv's flow-control response), waited thirty seconds on other errors, and322gave up a window after twelve consecutive failures (none occurred). The323state file \texttt{harvest\_state.json} records, per window, the number of324pages, of records and of seconds, and the harvest of the 107 windows produced325758 pages in about one hour.326327\paragraph{Datestamp semantics and deduplication.}328A point about OAI-PMH that affects the counts must be stated. The329\texttt{from}/\texttt{until} parameters select on the record's330\emph{datestamp}, which is the date of its last modification, not on its331submission date. A monthly window therefore contains every record touched in332that month, including old e-prints whose metadata were edited, and a record333edited in two months appears in two windows. The parser keeps, for each arXiv334identifier, the occurrence with the latest datestamp, skips records whose335header carries \texttt{status="deleted"}, normalises whitespace in titles and336abstracts, lower-cases the DOI, takes the first listed category as the337primary category, and records the set specifications. The result is338\LinkArxivCsRecords{} distinct records, of which339\LinkArxivCreatedTwoZeroOneEightTwoZeroTwoSix{} have a \texttt{created} date340(first submission) in 2018--2026; the difference consists of older e-prints341modified during the window, which are kept in the table for linkage but do342not belong to the observation window. The \texttt{created} date is the one343used as $d_{\mathrm{arx}}(w)$ in Part~IV\@. arXiv's metadata are offered for344reuse under its metadata terms; the e-prints themselves were not downloaded345in the acquisition described here.346347\subsection{ACL Anthology}348\label{sec:appB-acl}349350The ACL Anthology \citep{radev2013aclanthology, acl2024anthology}351distributes its complete bibliography as a BibTeX export. The variant with352abstracts (\texttt{anthology+}\allowbreak\texttt{abstracts.bib.gz}) was downloaded on3532~November 2026 (46{,}723{,}382 bytes; SHA-256 in354Table~\ref{tab:appB-acq}). BibTeX is not a regular language, so the parser355(\texttt{ingestion/acl\_parse.py}) walks the text by brace depth: it locates356each \texttt{@type\{} header, finds the matching closing brace, splits the357body at its first comma into the citation key (which is the Anthology358identifier, for instance \texttt{P19-1001} or \texttt{2023.acl-long.1}) and359the field list, and reads each field as a braced, quoted or bare value with360nested braces allowed. Fifteen fields are kept (title, author, editor,361booktitle, journal, month, year, address, publisher, URL, DOI, pages,362abstract, volume, number); braces inside values are removed and whitespace363normalised. Entries of type \texttt{comment}, \texttt{string} and364\texttt{preamble} are skipped. The export is released under CC~BY~4.0. The365number of entries parsed is \pending{AclParsedEntries}; the linkage step uses366only the \textit{inproceedings} and \textit{article} entries367(\LinkAclRecords{}), of which \LinkAclTwoZeroOneEightTwoZeroTwoSix{} fall in3682018--2026. The Anthology's role is a coverage check for natural-language369processing venues rather than a corpus source in its own right; an ACL entry370with no OpenAlex counterpart is counted in the linkage report but is never a371work of the corpus.372373\subsection{What was sought and not obtained}374\label{sec:appB-notobtained}375376The mandate lists sources beyond the four used, and the integrity rules377require that inaccessible data be recorded rather than worked around. The378record is as follows.379380\begin{description}381  \item[Semantic Scholar Academic Graph.] An anonymous request to the382  endpoint \texttt{graph/v1/paper/} for \texttt{arXiv:2303.08774} returned HTTP~429 with the383  instruction to apply for a key, and the datasets endpoint requires a key384  outright \citep{kinney2023s2ag}. No key was requested because the project385  rule is not to assume credentials and because applying would have made the386  acquisition depend on an approval outside the project's control. The387  consequence is the absence of the influential-citation and citation-intent388  signals from every analysis, stated wherever it matters.389  \item[OpenAlex API key.] A free key raises the daily budget from \$0.10 to390  \$1 and enables cursor paging. Obtaining it requires the principal391  investigator to confirm an e-mail; this was not done without instruction,392  and the increment of Section~\ref{sec:appB-increment} was run under the393  unkeyed budget. The snapshot route does not depend on the key.394  \item[Full texts.] No full text was downloaded during corpus construction.395  Abstracts come from the OpenAlex inverted index (and from arXiv and the396  Anthology for their records). The plan reserves lawful full-text access397  (arXiv e-prints at one request per three seconds, open-access locations)398  for at most 10{,}000 high-priority works in a later phase; the399  knowledge-graph extraction of Part~IV is abstract-based and labelled as400  such.401  \item[Crossref.] The REST API was reached (\texttt{works?filter=}\allowbreak\texttt{from-pub-date:2022-11-30\&rows=0}402  returned a count for all fields) and is used for the verification of every403  DOI cited in the manuscript and for sample cross-checks of types and404  citation counts; it was not used as a corpus source, OpenAlex already405  carrying its metadata.406  \item[Other sources listed in the mandate.] Kaggle arXiv snapshot,407  Microsoft Academic Graph, Papers with Code, institutional repositories: no Kaggle credentials were configured and408  OAI-PMH made the snapshot unnecessary; the Microsoft Academic Graph was409  discontinued in 2021; benchmark and code repositories are scoped to the410  reproducibility analyses of a later phase and are not bibliographic411  sources; institutional repositories enter only through OpenAlex's412  indexing of them, which the generic-deposit rule of413  Section~\ref{sec:appB-exclusions} then restricts.414\end{description}415416\subsection{PRISMA-S search record}417\label{sec:appB-prismas}418419PRISMA-S \citep{rethlefsen2021prismas} is written for keyword searches of420bibliographic databases. A scope-filtered mapping has no search string, but421its sixteen items can still be answered, and answering them makes explicit422which items have no counterpart. Table~\ref{tab:appB-prismas} gives the423record; the PRISMA~2020 flow itself \citep{page2021prisma, haddaway2022prismaflow}424is Figure~\ref{fig:prisma} and Table~\ref{tab:prisma} of Part~III\@.425426\begin{table}[htbp]427\centering\footnotesize428\caption{PRISMA-S items answered for the WP44 corpus.}429\label{tab:appB-prismas}430\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{0.5cm}>{\raggedright\arraybackslash}p{3.2cm}>{\raggedright\arraybackslash}X}431\toprule432No. & Item & This study \\433\midrule4341 & Database names & OpenAlex (works and entity snapshots), DBLP, arXiv, ACL Anthology; Crossref for verification only. \\4352 & Multi-database searching & Each source read in full under its own scope filter; no interface or vendor platform (direct bucket, dump and OAI access). \\4363 & Study registries & Not applicable. \\4374 & Online resources and browsing & None. \\4385 & Citation searching & None for identification; OpenAlex reference lists are analysed but do not add records to the corpus. \\4396 & Contacts & None. \\4407 & Other methods & None. \\4418 & Full search strategies & OpenAlex: predicate~\eqref{eq:appB-csfilter} with publication year 2018--2026, then identifier-set recovery of DBLP- and arXiv-linked works (Section~\ref{sec:appB-pass2}), then the API filter of Section~\ref{sec:appB-increment}. DBLP: whole dump, record types listed in Section~\ref{sec:appB-dblp}, years 2018--2026 at linkage. arXiv: OAI set \texttt{cs}, datestamps 2018-01-01 to 2026-11-02, \texttt{created} in 2018--2026 at linkage. ACL: whole export, types inproceedings and article, years 2018--2026 at linkage. \\4429 & Limits and restrictions & Year window only; no language, type or country restriction at search time (types are an eligibility criterion, Section~\ref{sec:appB-exclusions}). \\44310 & Search filters & None beyond the scope predicates; the OpenAlex topic classifier is the instrument that defines ``computer science'' on that layer and its properties are discussed in Part~III\@. \\44411 & Prior work & No search strategy was reused from a prior review. \\44512 & Updates & One update by API increment to 2026-11-02; no further update is planned for this version. \\44613 & Dates of searches & OpenAlex snapshot released 2026-10-14, read 2026-11-02; DBLP dump 2026-11-02; arXiv harvest 2026-11-02 to 03; ACL export 2026-11-02; API increment from 2026-11-03. \\44714 & Peer review of the search & No librarian review; the strategy is preregistered in \texttt{protocol/PROTOCOL.md} (P3) and audited by the independent agents of Phase~8. \\44815 & Total records & OpenAlex \PrismaIdentifiedOpenalexCsTopicTwoZeroOneEightTwoZeroTwoSixPassOne{} (pass 1); DBLP \PrismaIdentifiedDblpTwoZeroOneEightTwoZeroTwoSix{}; arXiv \PrismaIdentifiedArxivCsTwoZeroOneEightTwoZeroTwoSix{}; ACL \PrismaIdentifiedAclTwoZeroOneEightTwoZeroTwoSix{} (all 2018--2026). \\44916 & Deduplication & Deterministic record linkage across sources (Section~\ref{sec:appB-linkage}) and preprint--publication merging within OpenAlex (Part~III, Part~IV); OpenAlex works are the counting unit; \PrismaExcludedEFiveDuplicate{} preprint members of merged pairs are flagged E5. \\450\bottomrule451\end{tabularx}452\end{table}453454\subsection{Exclusion codes and their predicates}455\label{sec:appB-exclusions}456457The eligibility stage (\texttt{systematic\_review/define\_corpus.py}) computes458seven exclusion flags and two membership flags as columns of the table459\texttt{corpus\_works}, each a SQL expression over the normalised OpenAlex460record and the linkage tables. Table~\ref{tab:appB-codes} paraphrases the461predicates. Three lists on which they depend are reproduced verbatim from the462script, because their content is the substance of the type rule and of463amendment A-002.464465\begin{itemize}466  \item $T_{\mathrm{keep}}$ (types kept without further condition):467  \texttt{article}, \texttt{conference-}\allowbreak\texttt{paper}, \texttt{preprint},468  \texttt{book-}\allowbreak\texttt{chapter}, \texttt{book}, \texttt{dissertation},469  \texttt{report}, \texttt{review}, \texttt{software-}\allowbreak\texttt{paper}.470  \item $T_{\mathrm{hard}}$ (types excluded even for DBLP-linked records):471  \texttt{editorial}, \texttt{erratum}, \texttt{retraction},472  \texttt{peer-}\allowbreak\texttt{review}, \texttt{paratext}, \texttt{conference-}\allowbreak\texttt{abstract},473  \texttt{reference-}\allowbreak\texttt{entry}, \texttt{dataset}, \texttt{software},474  \texttt{supplementary-}\allowbreak\texttt{materials}, \texttt{libguides}, \texttt{letter},475  \texttt{grant}, \texttt{standard}.476  \item $\mathcal{G}$ (generic deposit platforms, matched on the primary477  source's display name): ``Zenodo (CERN European Organization for Nuclear478  Research)'' and ``Zenodo''; ``Figshare''; ``Institutional Repositories479  DataBase (IRDB)''; ``LA Referencia (Red Federada de Repositorios480  Institucionales de Publicaciones Cient\'{\i}ficas)''; ``Dialnet (Universidad de481  la Rioja)''; ``DOAJ (DOAJ: Directory of Open Access Journals)''; ``OSTI OAI482  (U.S. Department of Energy Office of Scientific and Technical483  Information)''.484\end{itemize}485486\begin{table}[htbp]487\centering\footnotesize488\caption{Exclusion and membership codes of \texttt{define\_corpus.py}, with their predicates paraphrased from SQL. $w$ is an OpenAlex record; \textit{in\_dblp}, \textit{in\_acl} and \textit{arxiv\_linked} are read from the link tables; $t_0$ = 2018-01-01, $t_1$ = 2026-11-02.}489\label{tab:appB-codes}490\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{0.9cm}>{\raggedright\arraybackslash}p{3.4cm}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}p{1.9cm}}491\toprule492Code & Column & Predicate (true = flagged) & Marginal count \\493\midrule494E1 & \texttt{e1\_outside\_window} & \texttt{publication\_date} is null, or $< t_0$, or $> t_1$ & \PrismaExcludedEOneOutsideWindow{} \\495E2 & \texttt{e2\_type} & $\tau(w) \notin T_{\mathrm{keep}}$ and not (\textit{in\_dblp} and $\tau(w) \notin T_{\mathrm{hard}}$); a null type counts as outside $T_{\mathrm{keep}}$ & \PrismaExcludedETwoType{} \\496E3 & \texttt{e3\_xpac} & \texttt{is\_xpac} and not \textit{in\_dblp} & \PrismaExcludedEThreeXpac{} \\497--- & \texttt{xpac\_kept\_dblp} & \texttt{is\_xpac} and \textit{in\_dblp} (kept, flagged) & \PrismaExcludedXpacKeptDblp{} \\498E4 & \texttt{e4\_no\_title} & title null or empty after trimming & \PrismaExcludedEFourNoTitle{} \\499E5 & \texttt{e5\_duplicate} & $w$ is the preprint member of a surviving preprint--published pair (Part~IV, eq.~\eqref{eq:duppair}) & \PrismaExcludedEFiveDuplicate{} \\500E6 & \texttt{e6\_retracted} & \texttt{is\_retracted} (flag only; not part of eligibility) & \PrismaExcludedESixRetracted{} \\501E7 & \texttt{e7\_generic\_deposit} & primary source name $\in \mathcal{G}$ and not \textit{in\_dblp} and no linked arXiv record & \pending{PrismaExcludedESevenGenericDeposit} \\502core & \texttt{cs\_core} & \textit{in\_dblp} or \texttt{cs\_primary} or (\texttt{cs\_any} and linked arXiv primary category begins with \texttt{cs.}) & \PrismaCsCoreEligible{} eligible \\503pen. & \texttt{cs\_penumbra} & \texttt{cs\_any} and not \texttt{cs\_core} & \PrismaCsPenumbraEligibleOtherwise{} otherwise eligible \\504\midrule505--- & \texttt{eligible} & $\neg$E1 $\wedge$ $\neg$E2 $\wedge$ $\neg$E3 $\wedge$ $\neg$E4 $\wedge$ $\neg$E5 $\wedge$ $\neg$E7 $\wedge$ core & \PrismaCsCoreEligible{} \\506\bottomrule507\end{tabularx}508\end{table}509510Three further columns are derived in the same statement and are used511throughout the manuscript. The first date, \texttt{first\_date}, is the least512of the publication date, the merged preprint's date and the arXiv creation513date, except that a publication date falling on 1~January is treated as514year-only precision and is overridden by either of the other two when515available; \texttt{date\_precision} is \texttt{year} when the date falls on5161~January and no finer date exists, \texttt{month} otherwise; and517\texttt{period} is \texttt{pre}, \texttt{baseline}, \texttt{post} or518\texttt{future} according to whether the first date precedes $t_0$, lies in519$[t_0, \text{2022-11-30})$, in $[\text{2022-11-30}, t_1]$, or after $t_1$.520The PRISMA counts are computed twice, marginally (the column of521Table~\ref{tab:appB-codes}) and sequentially in the order E1, E2, E3, E4, E5,522E7, and the script asserts that the loaded total equals the sum of the523sequential exclusions and the after-exclusion count524(\PrismaAfterExclusions{}); the macro set of Part~III reports both. The E6 and525penumbra rows are not exclusions and do not enter the sum. The numbering526conflict between the protocol's E7 (penumbra) and the code's E7527(generic deposit) is resolved by amendment A-005 in favour of the code.528529\subsection{Record-linkage cascade and thresholds}530\label{sec:appB-linkage}531532Part~IV states the cascade in prose; Table~\ref{tab:appB-cascade} gives it as533executed, step by step, with every threshold. The normalised title534$\eta(\cdot)$ lower-cases and deletes every character outside $[a$--$z0$--$9]$;535``unique'' means that the normalised title occurs exactly once on that side536among the records still unlinked at that step (for OpenAlex in the arXiv and537ACL cascades, unique among all OpenAlex records, the exclusion of already538linked identifiers being applied to the pair instead); the length condition539is on the raw title for DBLP and OpenAlex and on the normalised title for540arXiv and ACL\@. The DBLP title step adds a first-author check that the other541cascades do not have: the last whitespace-separated token of the first542author's name on each side must coincide when both are present. Each link543carries its method label, which is the stratum of the precision sample of544Part~IV; the counts per method are in \texttt{data/metadata/linkage\_report.json}545and Table~\ref{tab:linkage}.546547\begin{table}[htbp]548\centering\footnotesize549\caption{The deterministic linkage cascade of \texttt{ingestion/link\_sources.py}. Steps within a cascade apply only to records unlinked by the previous steps.}550\label{tab:appB-cascade}551\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{1.6cm}>{\raggedright\arraybackslash}p{1.4cm}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}p{1.8cm}}552\toprule553Cascade & Step & Join condition and thresholds & Method label \\554\midrule555\multirow{3}{*}{DBLP--OA}556 & 1 & equal lower-cased DOI; all matching pairs kept (many-to-many allowed) & \texttt{doi} \\557 & 2 & equal arXiv identifier (DBLP: parsed from \texttt{ee} URLs by \texttt{arxiv.org/abs/([\^{} ]+)}; both sides stripped of \texttt{v[0-9]+\$}); both records unlinked & \texttt{arxiv} \\558 & 3 & equal $\eta$, raw title $\ge 25$ characters, $\eta$ unique on both sides, $|y_{\mathrm{DBLP}} - y_{\mathrm{OA}}| \le 1$, first-author surnames equal when both present & \texttt{title\_year} \\559\midrule560\multirow{3}{*}{arXiv--OA}561 & 1 & equal arXiv identifier (OpenAlex side: \texttt{ids.arxiv}, else DOI prefix \texttt{10.48550/arxiv.}, else \texttt{arxiv.org/abs|pdf/} in any location URL); all pairs kept & \texttt{arxiv\_id} \\562 & 2 & equal lower-cased DOI; arXiv record unlinked & \texttt{doi} \\563 & 3 & equal $\eta$, $|\eta| \ge 25$, unique on both sides, $|y_{\mathrm{created}} - y_{\mathrm{OA}}| \le 2$, OpenAlex record unlinked & \texttt{title\_year} \\564\midrule565\multirow{3}{*}{ACL--OA}566 & 1 & equal lower-cased DOI & \texttt{doi} \\567 & 2 & Anthology identifier equal to the segment after \texttt{aclanthology.org/} in any OpenAlex landing-page URL (with or without trailing slash); ACL record unlinked & \texttt{url} \\568 & 3 & equal $\eta$, $|\eta| \ge 25$, unique on both sides, $|y_{\mathrm{ACL}} - y_{\mathrm{OA}}| \le 1$, OpenAlex record unlinked & \texttt{title\_year} \\569\bottomrule570\end{tabularx}571\end{table}572573The record types admitted on the DBLP side are \textit{article},574\textit{inproceedings}, \textit{incollection}, \textit{book},575\textit{phdthesis} and \textit{data} with year 2018--2026; on the ACL side576\textit{inproceedings} and \textit{article}; on the arXiv side every record577of the harvest, the year condition being applied through the \texttt{created}578year in the title step only. The linkage runs inside one DuckDB database579with 24 threads and a 60~GB memory limit and completes in about ten seconds580on the compacted tables, which is why it was possible to rerun it after each581amendment rather than patch its output. The precision sample of 500 pairs582(250 DOI, 200 title--year, 50 arXiv, the last stratum empty for the reason583given in Part~IV) is drawn by \texttt{systematic\_review/linkage\_sample.py}584with seed 44, and its automatic agreement checks (title similarity at least5850.9, year difference at most one, first-author surname agreement) are the586interim figure until the principal investigator's review (checkpoint H-1).587588\subsection{Reproducing the acquisition}589\label{sec:appB-repro}590591The acquisition can be repeated from the runbook in six commands, in the592order of Table~\ref{tab:appB-acq}, on a machine with Python~3.12 and the593locked environment of \texttt{reproducibility/}\allowbreak\texttt{environment\_M3U96b.txt}:594\begin{enumerate}595  \item \texttt{openalex\_snapshot.py} with the manifest copy (then once more with \texttt{--id-list});596  \item \texttt{openalex\_entities.py};597  \item \texttt{dblp\_parse.py};598  \item \texttt{arxiv\_oai.py};599  \item \texttt{acl\_parse.py};600  \item \texttt{openalex\_api\_increment.py}, followed by \texttt{ingest\_api\_increment.py}.601\end{enumerate}602Two of these are not exactly repeatable and the reader should know which.603The OpenAlex bucket retains only recent releases, so a later reader must604either locate the 2026-10-14 release or accept that a newer release will605differ in record counts and in the nightly-recomputed fields (citation606counts, FWCI); the per-file digests in the ledger allow a reader with the607same release to prove identity, and a reader with a different release to608quantify the difference by record count. The arXiv OAI feed is likewise609live: a re-harvest of the same datestamp windows returns records as they now610are, and the raw pages archived with the project are the only way to recover611the state of 2--3~November 2026. The DBLP dump and the ACL export are dated612files whose hashes are published here, and a copy of each is kept with the613project's raw layer.614
Files · 2ab47878d158af98e705ea50dfb2476de76ce03d
Menu

Theme