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 108~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\PrismaIdentifiedOpenalexPassOneCsTopicFilter{}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, namely the works of the slim table outside the retained subset that182an unlinked DBLP DOI or an unlinked arXiv DOI reaches. The list is re-derived183at every linkage run; after the run on the union of both passes it holds only184the residue that the second pass left outside the subset185(\LinkNeededOaIdsSecondPassDblpDoi{} identifiers reached through a DBLP DOI186and \LinkNeededOaIdsSecondPassArxivDoi{} through an arXiv DOI). Given the187list, \texttt{openalex\_snapshot.py --id-list} streams every part file again,188keeping exactly the records whose identifier is in the set and writing no slim189rows. The identifier set is loaded in each worker process (macOS uses the190\texttt{spawn} start method, so module globals are rebuilt per worker), the191same download, length and SHA-256 discipline applies, and the output lands in192\texttt{works\_pass2/cs/} with its own ledger. The recovered records are193normalised with the suffix \texttt{\_p2} and the linkage is rerun on the194union of both passes (\texttt{ingestion/run\_pass2\_chain.sh}). The second195pass costs the same bandwidth as the first---about one hour and fifty minutes196of streaming---because an identifier lookup cannot be pushed to the bucket;197the price was accepted because the alternative, fetching the identifiers one198by one through the API, would have exceeded the public budget by two orders of199magnitude. The PRISMA counts of Part~III are generated from the union of both passes:200the first pass retained \PrismaIdentifiedOpenalexPassOneCsTopicFilter{}201records through the topic filter, the second added202\PrismaIdentifiedOpenalexPassTwoDblpArxivLinked{} records reached through203DBLP or arXiv identifiers, and the identification total of the PRISMA figure204is their sum, \PrismaIdentifiedOpenalexTotalRecordsLoaded{} records loaded205into $\mathcal{W}_0$.206207\subsection{OpenAlex API increment}208\label{sec:appB-increment}209210The snapshot ends on 14~October 2026 and the observation window on2112~November 2026. The gap was covered by the REST API212(\texttt{https://api.openalex.org/works}) without a key. Two constraints of the213unkeyed API shaped the harvest and are recorded here because they bound what214the increment can contain. First, cursor paging requires a key, so the215harvest used basic page numbering, which the API caps at 10{,}000 results per216filter expression; the harvest is therefore \emph{sliced}, one request series217per publication day $d$ and per primary-field class, with the filter218\begin{quote}\ttfamily219topics.field.id:17, from\_publication\_date:$d$, to\_publication\_date:$d$,220primary\_topic.field.id:17 \quad (slice ``cs\_primary'')\\221topics.field.id:17, from\_publication\_date:$d$, to\_publication\_date:$d$,222primary\_topic.field.id:!17 \quad (slice ``cs\_secondary'')223\end{quote}224for every day from 2026-10-01 to 2026-11-02 (the start was placed two weeks225before the snapshot date to absorb records that OpenAlex back-dates), 200226results per page, a \texttt{select} list of the fields used by the227normaliser (identifiers, dates, type, language, locations, open access,228authorships, topics, keywords, referenced works, citation counts, abstract229inverted index, flags, FWCI, funders, indexed-in, full-text flag, APC), the230polite-pool \texttt{mailto} parameter and a descriptive user agent. A slice231whose reported \texttt{count} exceeds 10{,}000 is marked \texttt{truncated} in232the ledger; the two-way split by primary field was chosen because a single233day of computer-science output in 2026 can exceed the cap while each half234rarely does. Second, the unkeyed API is metered: list-and-filter calls are235charged at about \$0.0001 each against a shared budget of \$0.10 per day,236and the script stops at \$0.085 per day (a margin for the free single-record237lookups used for reference verification), sleeps 0.12~s between calls, and238halts on HTTP~429, 403 or any 5xx response so that the next day's run resumes239from the ledger. Every page is stored verbatim as240\texttt{api\_increment/pages/<day>\_<slice>/page\_NNNN.json}; the ledger241(\texttt{api\_increment/ledger.json}) records per slice the pages, records,242reported count, completion and truncation flags, and globally the number of243calls and the cost. At the time of writing the harvest had used the budget244of several consecutive days and was not complete: \IncrementApiCalls{} calls245had retrieved \IncrementRecordsRetrieved{} records at a cost of246USD~\IncrementCostUsd{}, and \IncrementSlicesDone{} of the sixty-six247day-slices (thirty-three days times two strata) were closed. The number of248truncated slices is \IncrementTruncatedSlices{}: the \texttt{cs\_secondary}249stratum of 16~October 2026 reported a count nearly four times the25010{,}000-record paging limit, so that roughly a quarter of its records were251retrieved. The remaining slices will be reported from the ledger when they252close. The conversion step (\texttt{ingest\_api\_increment.py})253rewrites the pages into the same \texttt{.jsonl.zst} part format as the254snapshot filter (\texttt{works\_pass3/}), skipping any work whose identifier255is already in the pass-1 or pass-2 tables and writing an256\texttt{increment\_report.json} (pages, new works, works already present); the257number of works added by the increment is \pending{IncrementNewWorks}. Two258limitations follow from the design and are not recoverable from the ledger: a259truncated slice loses the records beyond the 10{,}000th in the API's default260ordering, and a record created after 2~November 2026 with a publication date261inside the window is absent whatever its date. Both affect only the last262weeks of a window that is already treated as right-censored (Part~III), so no263analysis depends on them.264265\subsection{DBLP}266\label{sec:appB-dblp}267268\paragraph{Dump and version.}269DBLP \citep{ley2002dblp, ley2009dblp} publishes its whole database as a270single XML file. The file read is \texttt{dblp.xml.gz} as served on2712~November 2026 (file timestamp 22:39 UTC), 1{,}116{,}270{,}104 bytes, together272with the document type definition \texttt{dblp.dtd} and the published273checksum file \texttt{dblp.xml.gz.md5}. The MD5 of the whole file,274\texttt{ff1c424c3b4cef36582f647c36df27c0}, was computed and compared with the275published value before parsing; the parse report additionally stores the file276size and an MD5 of the first gibibyte as a quick identity check for later277sessions. DBLP is released under the Open Data Commons Attribution licence278(ODC-BY~1.0), which permits the redistribution of derived data with279attribution. The download from \texttt{dblp.org} proceeded at about 12~MB/s,280the slowest link of the whole acquisition.281282\paragraph{Parsing rule.}283The DTD matters: DBLP titles use XML entities for accented characters, and a284parser that does not resolve them against \texttt{dblp.dtd} corrupts titles285and therefore the title-based linkage. The parser286(\texttt{ingestion/dblp\_parse.py}) is \texttt{lxml.etree.iterparse} over the287gzip stream with \texttt{load\_dtd=True}, \texttt{resolve\_entities=True} and288\texttt{huge\_tree=True}, run from the directory containing the DTD because289the dump references it relatively. It emits one row per record of the types290\textit{article}, \textit{inproceedings}, \textit{proceedings}, \textit{book},291\textit{incollection}, \textit{phdthesis}, \textit{mastersthesis} and292\textit{data}; person records (\texttt{www}) are consumed and discarded. For293each record it keeps the key, type, modification date, publication-type294attribute, title (concatenated text of the title element, so that embedded295markup such as subscripts is flattened), year, author names and DBLP person296identifiers, editors, journal, booktitle, volume, number, pages, publisher,297series, the DOI (taken from the first electronic-edition URL beginning with298\texttt{https://doi.org/}, lower-cased), the full list of electronic-edition299URLs, the URL, the cross-reference to the proceedings record, and the300\emph{stream}, the part of the key before its last slash301(\texttt{journals/tocs}, \texttt{conf/nips}), which is DBLP's venue identifier302and the unit on which the ontology's venue signals are defined. All years are303parsed; counts by type and by year for the entire archive are written to304\texttt{dblp\_parse\_report.json}, and the records of 2018--2026305(\LinkDblpTwoZeroOneEightTwoZeroTwoSix{} after the type restriction of the306linkage step) are the ones that enter the corpus. The total number of307publication records in the dump is \DblpParsedRecords{}. A second308script (\texttt{dblp\_streams.py}) lists the streams with at least one hundred309records of type article or inproceedings in 2018--2026, with their first and310last years and a sample journal or booktitle name; this list311(\texttt{taxonomy/dblp\_streams\_2018\_2026.csv}) is the input of the312venue-to-category mapping and is not itself a corpus filter.313314\subsection{arXiv}315\label{sec:appB-arxiv}316317\paragraph{Protocol and windows.}318arXiv \citep{ginsparg2011arxiv} metadata were harvested through its OAI-PMH319endpoint \texttt{https://oaipmh.arxiv.org/oai} with the verb320\texttt{ListRecords}, the set \texttt{cs} and the metadata prefix321\texttt{arXiv}, which returns arXiv's own schema (identifier, dates, title,322abstract, authors with forenames and keynames, categories, comments, journal323reference, DOI, licence, report number, MSC and ACM classes). The harvest was324sliced into calendar-month windows on the OAI \texttt{from}/\texttt{until}325parameters from 2018-01-01 to 2026-11-02; each window was walked with the326resumption token until exhausted, and every response page was written327verbatim to \texttt{raw/<window>/page\_NNNN.xml} before being parsed, so that328the Parquet table can be rebuilt from the raw pages329(\texttt{--parse-only}) without touching the network. The harvester waited3303.5~s between requests, honoured the \texttt{Retry-After} header on HTTP~503331(arXiv's flow-control response), waited thirty seconds on other errors, and332gave up a window after twelve consecutive failures (none occurred). The333state file \texttt{harvest\_state.json} records, per window, the number of334pages, of records and of seconds, and the harvest of the 107 windows produced335758 pages in about one hour.336337\paragraph{Datestamp semantics and deduplication.}338A point about OAI-PMH that affects the counts must be stated. The339\texttt{from}/\texttt{until} parameters select on the record's340\emph{datestamp}, which is the date of its last modification, not on its341submission date. A monthly window therefore contains every record touched in342that month, including old e-prints whose metadata were edited, and a record343edited in two months appears in two windows. The parser keeps, for each arXiv344identifier, the occurrence with the latest datestamp, skips records whose345header carries \texttt{status="deleted"}, normalises whitespace in titles and346abstracts, lower-cases the DOI, takes the first listed category as the347primary category, and records the set specifications. The result is348\LinkArxivCsRecords{} distinct records, of which349\LinkArxivCreatedTwoZeroOneEightTwoZeroTwoSix{} have a \texttt{created} date350(first submission) in 2018--2026; the difference consists of older e-prints351modified during the window, which are kept in the table for linkage but do352not belong to the observation window. The \texttt{created} date is the one353used as $d_{\mathrm{arx}}(w)$ in Part~IV\@. arXiv's metadata are offered for354reuse under its metadata terms; the e-prints themselves were not downloaded355in the acquisition described here.356357\subsection{ACL Anthology}358\label{sec:appB-acl}359360The ACL Anthology \citep{radev2013aclanthology, acl2024anthology}361distributes its complete bibliography as a BibTeX export. The variant with362abstracts (\texttt{anthology+}\allowbreak\texttt{abstracts.bib.gz}) was downloaded on3632~November 2026 (46{,}723{,}382 bytes; SHA-256 in364Table~\ref{tab:appB-acq}). BibTeX is not a regular language, so the parser365(\texttt{ingestion/acl\_parse.py}) walks the text by brace depth: it locates366each \texttt{@type\{} header, finds the matching closing brace, splits the367body at its first comma into the citation key (which is the Anthology368identifier, for instance \texttt{P19-1001} or \texttt{2023.acl-long.1}) and369the field list, and reads each field as a braced, quoted or bare value with370nested braces allowed. Fifteen fields are kept (title, author, editor,371booktitle, journal, month, year, address, publisher, URL, DOI, pages,372abstract, volume, number); braces inside values are removed and whitespace373normalised. Entries of type \texttt{comment}, \texttt{string} and374\texttt{preamble} are skipped. The export is released under CC~BY~4.0. The375number of entries parsed is \AclParsedEntries{}; the linkage step uses376only the \textit{inproceedings} and \textit{article} entries377(\LinkAclRecords{}), of which \LinkAclTwoZeroOneEightTwoZeroTwoSix{} fall in3782018--2026. The Anthology's role is a coverage check for natural-language379processing venues rather than a corpus source in its own right; an ACL entry380with no OpenAlex counterpart is counted in the linkage report but is never a381work of the corpus.382383\subsection{What was sought and not obtained}384\label{sec:appB-notobtained}385386The mandate lists sources beyond the four used, and the integrity rules387require that inaccessible data be recorded rather than worked around. The388record is as follows.389390\begin{description}391 \item[Semantic Scholar Academic Graph.] An anonymous request to the392 endpoint \texttt{graph/v1/paper/} for \texttt{arXiv:2303.08774} returned HTTP~429 with the393 instruction to apply for a key, and the datasets endpoint requires a key394 outright \citep{kinney2023s2ag}. No key was requested because the project395 rule is not to assume credentials and because applying would have made the396 acquisition depend on an approval outside the project's control. The397 consequence is the absence of the influential-citation and citation-intent398 signals from every analysis, stated wherever it matters.399 \item[OpenAlex API key.] A free key raises the daily budget from \$0.10 to400 \$1 and enables cursor paging. Obtaining it requires the principal401 investigator to confirm an e-mail; this was not done without instruction,402 and the increment of Section~\ref{sec:appB-increment} was run under the403 unkeyed budget. The snapshot route does not depend on the key.404 \item[Full texts.] No full text was downloaded during corpus construction.405 Abstracts come from the OpenAlex inverted index (and from arXiv and the406 Anthology for their records). The plan reserves lawful full-text access407 (arXiv e-prints at one request per three seconds, open-access locations)408 for at most 10{,}000 high-priority works in a later phase; the409 knowledge-graph extraction of Part~IV is abstract-based and labelled as410 such.411 \item[Crossref.] The REST API was reached (\texttt{works?filter=}\allowbreak\texttt{from-pub-date:2022-11-30\&rows=0}412 returned a count for all fields) and is used for the verification of every413 DOI cited in the manuscript and for sample cross-checks of types and414 citation counts; it was not used as a corpus source, OpenAlex already415 carrying its metadata.416 \item[Other sources listed in the mandate.] Kaggle arXiv snapshot,417 Microsoft Academic Graph, Papers with Code, institutional repositories: no Kaggle credentials were configured and418 OAI-PMH made the snapshot unnecessary; the Microsoft Academic Graph was419 discontinued in 2021; benchmark and code repositories are scoped to the420 reproducibility analyses of a later phase and are not bibliographic421 sources; institutional repositories enter only through OpenAlex's422 indexing of them, which the generic-deposit rule of423 Section~\ref{sec:appB-exclusions} then restricts.424\end{description}425426\subsection{PRISMA-S search record}427\label{sec:appB-prismas}428429PRISMA-S \citep{rethlefsen2021prismas} is written for keyword searches of430bibliographic databases. A scope-filtered mapping has no search string, but431its sixteen items can still be answered, and answering them makes explicit432which items have no counterpart. Table~\ref{tab:appB-prismas} gives the433record; the PRISMA~2020 flow itself \citep{page2021prisma, haddaway2022prismaflow}434is Figure~\ref{fig:prisma} and Table~\ref{tab:prisma} of Part~III\@.435436\begin{table}[htbp]437\centering\footnotesize438\caption{PRISMA-S items answered for the WP44 corpus.}439\label{tab:appB-prismas}440\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{0.5cm}>{\raggedright\arraybackslash}p{3.2cm}>{\raggedright\arraybackslash}X}441\toprule442No. & Item & This study \\443\midrule4441 & Database names & OpenAlex (works and entity snapshots), DBLP, arXiv, ACL Anthology; Crossref for verification only. \\4452 & Multi-database searching & Each source read in full under its own scope filter; no interface or vendor platform (direct bucket, dump and OAI access). \\4463 & Study registries & Not applicable. \\4474 & Online resources and browsing & None. \\4485 & Citation searching & None for identification; OpenAlex reference lists are analysed but do not add records to the corpus. \\4496 & Contacts & None. \\4507 & Other methods & None. \\4518 & 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. \\4529 & 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}). \\45310 & 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\@. \\45411 & Prior work & No search strategy was reused from a prior review. \\45512 & Updates & One update by API increment to 2026-11-02; no further update is planned for this version. \\45613 & 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. \\45714 & 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. \\45815 & Total records & OpenAlex \PrismaIdentifiedOpenalexTotalRecordsLoaded{} (\PrismaIdentifiedOpenalexPassOneCsTopicFilter{} in pass 1, \PrismaIdentifiedOpenalexPassTwoDblpArxivLinked{} in pass 2); DBLP \PrismaIdentifiedDblpTwoZeroOneEightTwoZeroTwoSix{}; arXiv \PrismaIdentifiedArxivCsTwoZeroOneEightTwoZeroTwoSix{}; ACL \PrismaIdentifiedAclTwoZeroOneEightTwoZeroTwoSix{} (all 2018--2026). \\45916 & 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. \\460\bottomrule461\end{tabularx}462\end{table}463464\subsection{Exclusion codes and their predicates}465\label{sec:appB-exclusions}466467The eligibility stage (\texttt{systematic\_review/define\_corpus.py}) computes468seven exclusion flags and two membership flags as columns of the table469\texttt{corpus\_works}, each a SQL expression over the normalised OpenAlex470record and the linkage tables. Table~\ref{tab:appB-codes} paraphrases the471predicates. Three lists on which they depend are reproduced verbatim from the472script, because their content is the substance of the type rule and of473amendment A-002.474475\begin{itemize}476 \item $T_{\mathrm{keep}}$ (types kept without further condition):477 \texttt{article}, \texttt{conference-}\allowbreak\texttt{paper}, \texttt{preprint},478 \texttt{book-}\allowbreak\texttt{chapter}, \texttt{book}, \texttt{dissertation},479 \texttt{report}, \texttt{review}, \texttt{software-}\allowbreak\texttt{paper}.480 \item $T_{\mathrm{hard}}$ (types excluded even for DBLP-linked records):481 \texttt{editorial}, \texttt{erratum}, \texttt{retraction},482 \texttt{peer-}\allowbreak\texttt{review}, \texttt{paratext}, \texttt{conference-}\allowbreak\texttt{abstract},483 \texttt{reference-}\allowbreak\texttt{entry}, \texttt{dataset}, \texttt{software},484 \texttt{supplementary-}\allowbreak\texttt{materials}, \texttt{libguides}, \texttt{letter},485 \texttt{grant}, \texttt{standard}.486 \item $\mathcal{G}$ (generic deposit platforms, matched on the primary487 source's display name): ``Zenodo (CERN European Organization for Nuclear488 Research)'' and ``Zenodo''; ``Figshare''; ``Institutional Repositories489 DataBase (IRDB)''; ``LA Referencia (Red Federada de Repositorios490 Institucionales de Publicaciones Cient\'{\i}ficas)''; ``Dialnet (Universidad de491 la Rioja)''; ``DOAJ (DOAJ: Directory of Open Access Journals)''; ``OSTI OAI492 (U.S. Department of Energy Office of Scientific and Technical493 Information)''.494\end{itemize}495496\begin{table}[htbp]497\centering\footnotesize498\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.}499\label{tab:appB-codes}500\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{0.9cm}>{\raggedright\arraybackslash}p{3.4cm}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}p{1.9cm}}501\toprule502Code & Column & Predicate (true = flagged) & Marginal count \\503\midrule504E1 & \texttt{e1\_outside\_window} & \texttt{publication\_date} is null, or $< t_0$, or $> t_1$ & \PrismaExcludedEOneOutsideWindow{} \\505E2 & \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{} \\506E3 & \texttt{e3\_xpac} & \texttt{is\_xpac} and not \textit{in\_dblp} & \PrismaExcludedEThreeXpac{} \\507--- & \texttt{xpac\_kept\_dblp} & \texttt{is\_xpac} and \textit{in\_dblp} (kept, flagged) & \PrismaExcludedXpacKeptDblp{} \\508E4 & \texttt{e4\_no\_title} & title null or empty after trimming & \PrismaExcludedEFourNoTitle{} \\509E5 & \texttt{e5\_duplicate} & $w$ is the preprint member of a surviving preprint--published pair (Part~IV, eq.~\eqref{eq:duppair}) & \PrismaExcludedEFiveDuplicate{} \\510E6 & \texttt{e6\_retracted} & \texttt{is\_retracted} (flag only; not part of eligibility) & \PrismaExcludedESixRetracted{} \\511E7 & \texttt{e7\_generic\_deposit} & primary source name $\in \mathcal{G}$ and not \textit{in\_dblp} and no linked arXiv record & \PrismaExcludedESevenGenericDeposit{} \\512core & \texttt{cs\_core} & \textit{in\_dblp} or \texttt{cs\_primary} or linked arXiv primary category begins with \texttt{cs.} (amendment A-006) & \PrismaCsCoreEligible{} eligible \\513pen. & \texttt{cs\_penumbra} & \texttt{cs\_any} and not \texttt{cs\_core} & \PrismaCsPenumbraEligibleOtherwise{} otherwise eligible \\514\midrule515--- & \texttt{eligible} & $\neg$E1 $\wedge$ $\neg$E2 $\wedge$ $\neg$E3 $\wedge$ $\neg$E4 $\wedge$ $\neg$E5 $\wedge$ $\neg$E7 $\wedge$ core & \PrismaCsCoreEligible{} \\516\bottomrule517\end{tabularx}518\end{table}519520Three further columns are derived in the same statement and are used521throughout the manuscript. The first date, \texttt{first\_date}, is the least522of the publication date, the merged preprint's date and the arXiv creation523date, except that a publication date falling on 1~January is treated as524year-only precision and is overridden by either of the other two when525available; \texttt{date\_precision} is \texttt{year} when the date falls on5261~January and no finer date exists, \texttt{month} otherwise; and527\texttt{period} is \texttt{pre}, \texttt{baseline}, \texttt{post} or528\texttt{future} according to whether the first date precedes $t_0$, lies in529$[t_0, \text{2022-11-30})$, in $[\text{2022-11-30}, t_1]$, or after $t_1$.530The PRISMA counts are computed twice, marginally (the column of531Table~\ref{tab:appB-codes}) and sequentially in the order E1, E2, E3, E4, E5,532E7, and the script asserts that the loaded total equals the sum of the533sequential exclusions and the after-exclusion count534(\PrismaAfterExclusions{}); the macro set of Part~III reports both. Since535amendment A-006 the script also asserts that the after-exclusion count equals536the core plus the penumbra plus the records that belong to neither set537(\PrismaNeitherCoreNorPenumbraAfterExclusions{}, works with no538computer-science topic reached only through a DOI and not carrying an arXiv539\texttt{cs.} primary category). The E6 and540penumbra rows are not exclusions and do not enter the sum. The numbering541conflict between the protocol's E7 (penumbra) and the code's E7542(generic deposit) is resolved by amendment A-005 in favour of the code.543544\subsection{Record-linkage cascade and thresholds}545\label{sec:appB-linkage}546547Part~IV states the cascade in prose; Table~\ref{tab:appB-cascade} gives it as548executed, step by step, with every threshold. The normalised title549$\eta(\cdot)$ lower-cases and deletes every character outside $[a$--$z0$--$9]$;550``unique'' means that the normalised title occurs exactly once on that side551among the records still unlinked at that step (for OpenAlex in the arXiv and552ACL cascades, unique among all OpenAlex records, the exclusion of already553linked identifiers being applied to the pair instead); the length condition554is on the raw title for DBLP and OpenAlex and on the normalised title for555arXiv and ACL\@. The DBLP title step adds a first-author check that the other556cascades do not have: the last whitespace-separated token of the first557author's name on each side must coincide when both are present. Each link558carries its method label, which is the stratum of the precision sample of559Part~IV; the counts per method are in \texttt{data/metadata/linkage\_report.json}560and Table~\ref{tab:linkage}.561562\begin{table}[htbp]563\centering\footnotesize564\caption{The deterministic linkage cascade of \texttt{ingestion/link\_sources.py}. Steps within a cascade apply only to records unlinked by the previous steps.}565\label{tab:appB-cascade}566\begin{tabularx}{\textwidth}{>{\raggedright\arraybackslash}p{1.6cm}>{\raggedright\arraybackslash}p{1.4cm}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}p{1.8cm}}567\toprule568Cascade & Step & Join condition and thresholds & Method label \\569\midrule570\multirow{3}{*}{DBLP--OA}571 & 1 & equal lower-cased DOI; all matching pairs kept (many-to-many allowed) & \texttt{doi} \\572 & 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} \\573 & 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} \\574\midrule575\multirow{3}{*}{arXiv--OA}576 & 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} \\577 & 2 & equal lower-cased DOI; arXiv record unlinked & \texttt{doi} \\578 & 3 & equal $\eta$, $|\eta| \ge 25$, unique on both sides, $|y_{\mathrm{created}} - y_{\mathrm{OA}}| \le 2$, OpenAlex record unlinked & \texttt{title\_year} \\579\midrule580\multirow{3}{*}{ACL--OA}581 & 1 & equal lower-cased DOI & \texttt{doi} \\582 & 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} \\583 & 3 & equal $\eta$, $|\eta| \ge 25$, unique on both sides, $|y_{\mathrm{ACL}} - y_{\mathrm{OA}}| \le 1$, OpenAlex record unlinked & \texttt{title\_year} \\584\bottomrule585\end{tabularx}586\end{table}587588The record types admitted on the DBLP side are \textit{article},589\textit{inproceedings}, \textit{incollection}, \textit{book},590\textit{phdthesis} and \textit{data} with year 2018--2026; on the ACL side591\textit{inproceedings} and \textit{article}; on the arXiv side every record592of the harvest, the year condition being applied through the \texttt{created}593year in the title step only. The linkage runs inside one DuckDB database594with 24 threads and a 60~GB memory limit and completes in about ten seconds595on the compacted tables, which is why it was possible to rerun it after each596amendment rather than patch its output. The precision sample of 500 pairs597(250 DOI, 200 title--year, 50 arXiv, the last stratum empty for the reason598given in Part~IV) is drawn by \texttt{systematic\_review/linkage\_sample.py}599with seed 44, and its automatic agreement checks (title similarity at least6000.9, year difference at most one, first-author surname agreement) are the601interim figure until the principal investigator's review (checkpoint H-1).602603\subsection{Reproducing the acquisition}604\label{sec:appB-repro}605606The acquisition can be repeated from the runbook in six commands, in the607order of Table~\ref{tab:appB-acq}, on a machine with Python~3.12 and the608locked environment of \texttt{reproducibility/}\allowbreak\texttt{environment\_M3U96b.txt}:609\begin{enumerate}610 \item \texttt{openalex\_snapshot.py} with the manifest copy (then once more with \texttt{--id-list});611 \item \texttt{openalex\_entities.py};612 \item \texttt{dblp\_parse.py};613 \item \texttt{arxiv\_oai.py};614 \item \texttt{acl\_parse.py};615 \item \texttt{openalex\_api\_increment.py}, followed by \texttt{ingest\_api\_increment.py}.616\end{enumerate}617Two of these are not exactly repeatable and the reader should know which.618The OpenAlex bucket retains only recent releases, so a later reader must619either locate the 2026-10-14 release or accept that a newer release will620differ in record counts and in the nightly-recomputed fields (citation621counts, FWCI); the per-file digests in the ledger allow a reader with the622same release to prove identity, and a reader with a different release to623quantify the difference by record count. The arXiv OAI feed is likewise624live: a re-harvest of the same datestamp windows returns records as they now625are, and the raw pages archived with the project are the only way to recover626the state of 2--3~November 2026. The DBLP dump and the ACL export are dated627files whose hashes are published here, and a copy of each is kept with the628project's raw layer.629