SPB Git

spb/wp44_uqo

Public
30.9 KB · 320 lines latex
1% ============================================================================2% Appendix H — Reproducibility3% Written by Agent L (appendices A/E/H instance), 2026-11-03. Sources of truth: reproducibility/RUNBOOK.md,4% reproducibility/environment_M3U96b.txt, data/raw/README.md, reports/INCIDENTS.md, protocol/amendments.csv,5% protocol/PROTOCOL.md §P8, README.md, .gitignore, ingestion/*.py (ledger schemas). Every number here is a6% provenance fact (file counts, sizes, wall times, versions) taken from those files; no corpus result appears.7% ============================================================================89\section{Reproducibility}10\label{app:repro}1112The mandate requires that the corpus be reproducible from immutable raw snapshots and that every quantitative13claim trace to a script, an input artefact and a result file. This appendix records what a reader needs to14re-run the pipeline: the computing environment and the locked package versions, the ordered steps with their15commands, inputs, outputs and measured wall times, the mechanisms by which provenance is kept (manifests,16hash ledgers, verbatim raw pages, API ledgers), the incidents that occurred during the first run and what17they imply for a second one, the protocol amendments, and the licence of each source together with what is18and is not redistributed. The runbook (\texttt{reproducibility/\allowbreak RUNBOOK.\allowbreak md}) is the operational version of19this appendix and is updated whenever a step changes; where the two differ, the runbook is more recent.2021\subsection{Environment}22\label{app:repro-env}2324Everything described in the runbook was executed on one node of the MacLustr cluster, M3U96b (Mac Studio,25Apple M3~Ultra, 96~GB of unified memory, 103{,}079{,}215{,}104 bytes as recorded by the environment26capture; the capture records no core count, the runbook header states 28 cores and the cluster inventory 32,27and the discrepancy is left on record), in the working directory \texttt{\textasciitilde/\allowbreak wp44\_\allowbreak uqo}, under Python 3.12.15 in a virtual28environment created with \texttt{uv} from \texttt{requirements.\allowbreak txt} and then frozen to29\texttt{reproducibility/\allowbreak environment\_\allowbreak M3U96b.\allowbreak txt} (92 lines: 88 pinned packages, the interpreter version, the30operating-system version string, the processor and the memory size). The operating-system version string31captured in that file is 26.4, whereas the runbook header states macOS~27.0; the discrepancy is recorded here32rather than resolved, since the node was updated between captures and neither value affects the computation.33Two further nodes were used after the incidents of Section~\ref{app:repro-incidents}: M5M36 for the second34snapshot pass and M4M64c and M3U96a for embedding shards; they were set up from the same35\texttt{requirements.txt}, but no environment capture exists for them under \texttt{reproducibility/}, so the36identity of their environments with the one frozen on M3U96b is an assumption rather than a recorded fact.37Table~\ref{tab:appH-packages} lists the packages on which results depend; the complete list is the lock file.38No step requires credentials except the two LLM raters of the taxonomy validation and the knowledge-graph39extraction (an Anthropic API key read from a file outside the repository, never stored in it).4041\begin{table}[htbp]42\begin{singlespace}43\centering44\caption{Key packages of the locked environment (\texttt{reproducibility/\allowbreak environment\_\allowbreak M3U96b.\allowbreak txt}, Python 3.12.15).}45\label{tab:appH-packages}46\small47\begin{tabularx}{\textwidth}{l X l}48\toprule49Role & Package & Version \\50\midrule51Analytical database & \texttt{duckdb} \citep{raasveldt2019duckdb} & 2.0.0 \\52Columnar I/O & \texttt{pyarrow} & 26.0.0 \\53Data frames & \texttt{polars} / \texttt{pandas} & 2.1.0 / 3.1.0 \\54Numerics & \texttt{numpy} / \texttt{scipy} & 2.5.4 / 1.18.1 \\55Statistics, time series & \texttt{statsmodels} / \texttt{formulaic} & 0.15.0 / 1.2.2 \\56Change-point detection & \texttt{ruptures} \citep{truong2020ruptures} & 1.1.10 \\57Machine learning & \texttt{scikit-learn} & 1.9.1 \\58Embeddings & \texttt{torch} / \texttt{transformers} / \texttt{sentence-transformers} \citep{reimers2019sbert} & 2.15.0 / 5.22.1 / 6.2.0 \\59Model hub client & \texttt{huggingface-hub} & 2.4.0 \\60Dimensionality reduction, clustering & \texttt{umap-learn} / \texttt{hdbscan} / \texttt{pynndescent} & 0.5.12 / 0.8.44 / 0.7.0 \\61Networks & \texttt{networkx} / \texttt{igraph} & 3.7 / 1.0.0 \\62Parsing & \texttt{lxml} / \texttt{orjson} / \texttt{zstandard} / \texttt{regex} & 6.1.3 / 3.13.0 / 0.25.0 / 2026.9.29 \\63Ontology file & \texttt{pyyaml} & 6.0.3 \\64HTTP & \texttt{requests} / \texttt{httpx2} & 2.34.2 / 2.16.0 \\65LLM raters, extraction & \texttt{anthropic} & 2.4.0 \\66Figures & \texttt{matplotlib} & 3.11.2 \\67Tests & \texttt{pytest} & 9.2.0 \\68\bottomrule69\end{tabularx}70\end{singlespace}71\end{table}7273\subsection{Steps, commands and measured times}74\label{app:repro-steps}7576Table~\ref{tab:appH-steps} reproduces the runbook's step table. Commands are given relative to the repository77root with the virtual environment active; \texttt{\ldots} abbreviates arguments that are spelled out in the78runbook. Wall times are those measured during the first run and are reported as orientation, not as79guarantees: the snapshot download depends on network throughput (about 108~MB/s was sustained from the node80with eight parallel streams), and the steps marked ``to be measured'' had not completed at the time the81runbook was last updated because of the node freezes described below. Steps~1--5 are pure acquisition and82can be run independently and in any order; steps 6--10 are sequential; step~9 is a second acquisition pass83whose need is discovered at step~8 and which then repeats steps 6--8 and 10--11 with the suffix \texttt{\_\allowbreak p2}84(\texttt{ingestion/\allowbreak run\_\allowbreak pass2\_\allowbreak chain.\allowbreak sh}, which waits for a flag file written when the pass-2 outputs have85been copied back to the node). The API increment of Table~\ref{tab:appH-raw} (works published after the86snapshot cut-off) is converted to the snapshot's part format by \texttt{ingestion/\allowbreak ingest\_\allowbreak api\_\allowbreak increment.\allowbreak py}87and normalised by the same step~6; it is not yet a numbered step of the runbook.8889\begin{singlespace}90\begin{footnotesize}91\begin{longtable}{@{}p{0.5cm} p{5.6cm} p{2.3cm} p{4.6cm} p{2.3cm}@{}}92\caption{Pipeline steps (\texttt{reproducibility/\allowbreak RUNBOOK.\allowbreak md}), with the command, the inputs, the outputs and the wall time measured on M3U96b.}93\label{tab:appH-steps}\\94\toprule95\# & Step and command & Inputs & Outputs & Wall time \\96\midrule97\endfirsthead98\caption[]{Pipeline steps (continued).}\\99\toprule100\# & Step and command & Inputs & Outputs & Wall time \\101\midrule102\endhead103\midrule104\multicolumn{5}{r}{continued on next page}\\105\endfoot106\bottomrule107\endlastfoot1081 & OpenAlex works snapshot, pass~1 (CS filter + slim table): \texttt{python ingestion/\allowbreak openalex\_\allowbreak snapshot.\allowbreak py --out data/\allowbreak raw/\allowbreak openalex/\allowbreak works --manifest data/\allowbreak raw/\allowbreak openalex/\allowbreak works\_\allowbreak manifest\_\allowbreak 2026-10-14.\allowbreak json --workers 8} & public S3 bucket, release 2026-10-14 & \texttt{works/\allowbreak cs/\allowbreak *.\allowbreak jsonl.\allowbreak zst} (\PrismaIdentifiedOpenalexPassOneCsTopicFilter{} records), \texttt{works/\allowbreak slim/\allowbreak *.\allowbreak parquet} (\LinkSlimTotal{} rows), \texttt{ledger.\allowbreak sqlite} & 1~h 46~min at $\approx$108~MB/s \\1092 & OpenAlex entities: \texttt{python ingestion/\allowbreak openalex\_\allowbreak entities.\allowbreak py --out data/\allowbreak raw/\allowbreak openalex} & S3 & \texttt{topics/\allowbreak }, \ldots, \texttt{institutions/\allowbreak }; \texttt{entities\_\allowbreak ledger.\allowbreak json} & 5~min \\1103 & DBLP: \texttt{python ingestion/\allowbreak dblp\_\allowbreak parse.\allowbreak py --xml data/\allowbreak raw/\allowbreak dblp/\allowbreak dblp.\allowbreak xml.\allowbreak gz --out data/\allowbreak raw/\allowbreak dblp} & dump of 2026-11-02, MD5 verified & \texttt{dblp\_\allowbreak publications.\allowbreak parquet} (\DblpParsedRecords{} records), parse report & 4~min 45~s \\1114 & arXiv OAI-PMH: \texttt{python ingestion/\allowbreak arxiv\_\allowbreak oai.\allowbreak py --out data/\allowbreak raw/\allowbreak arxiv --from 2018-01-01 --until 2026-11-02} & OAI feed, set \texttt{cs} & raw XML pages (758), \texttt{arxiv\_\allowbreak cs.\allowbreak parquet} (\LinkArxivCsRecords{} records) & $\approx$1~h \\1125 & ACL Anthology: \texttt{python ingestion/\allowbreak acl\_\allowbreak parse.\allowbreak py --bib data/\allowbreak raw/\allowbreak acl/\allowbreak anthology+abstracts.\allowbreak bib.\allowbreak gz --out data/\allowbreak raw/\allowbreak acl} & BibTeX export & \texttt{acl\_\allowbreak anthology.\allowbreak parquet} (\AclParsedEntries{} entries) & 10~s \\1136 & Normalise: \texttt{python ingestion/\allowbreak build\_\allowbreak corpus.\allowbreak py --cs-dir data/\allowbreak raw/\allowbreak openalex/\allowbreak works/\allowbreak cs --out data/\allowbreak processed --workers 12} & step 1 & \texttt{tables/\allowbreak <table>/\allowbreak *.\allowbreak parquet} & 2~min 10~s \\1147 & Compact: \texttt{ulimit -n 65536; python ingestion/\allowbreak compact\_\allowbreak tables.\allowbreak py \textasciitilde/\allowbreak wp44\_\allowbreak uqo} & step 6 & \texttt{compact/\allowbreak <table>.\allowbreak parquet} & 30~s \\1158 & Link: \texttt{python ingestion/\allowbreak link\_\allowbreak sources.\allowbreak py --root \textasciitilde/\allowbreak wp44\_\allowbreak uqo} & steps 3--7 & \texttt{wp44.\allowbreak duckdb} with \texttt{link\_\allowbreak *}; \texttt{data/\allowbreak metadata/\allowbreak linkage\_\allowbreak report.\allowbreak json}; \texttt{needed\_\allowbreak oa\_\allowbreak ids.\allowbreak csv} & 10~s \\1169 & Second pass (DBLP- and arXiv-linked works outside the CS-topic filter): \texttt{python ingestion/\allowbreak openalex\_\allowbreak snapshot.\allowbreak py --out data/\allowbreak raw/\allowbreak openalex/\allowbreak works\_\allowbreak pass2 --manifest \ldots --workers 8 --id-list data/\allowbreak metadata/\allowbreak needed\_\allowbreak oa\_\allowbreak ids.\allowbreak csv}, then \texttt{ingestion/\allowbreak run\_\allowbreak pass2\_\allowbreak chain.\allowbreak sh} (steps 6--8 with suffix \texttt{\_\allowbreak p2}, then 10--11) & S3, step 8 & \texttt{works\_\allowbreak pass2/\allowbreak cs/\allowbreak *.\allowbreak jsonl.\allowbreak zst}, \texttt{compact/\allowbreak <table>\_\allowbreak p2.\allowbreak parquet} & $\approx$1~h 50~min \\11710 & Corpus definition: \texttt{python systematic\_\allowbreak review/\allowbreak define\_\allowbreak corpus.\allowbreak py --root \textasciitilde/\allowbreak wp44\_\allowbreak uqo} & step 8 & \texttt{corpus\_\allowbreak works}, \texttt{data/\allowbreak metadata/\allowbreak prisma\_\allowbreak counts.\allowbreak json} & 2~min \\11811 & Volume, collaboration, citations, graph: \texttt{bibliometrics/\allowbreak 01\_\allowbreak volume\_\allowbreak series.\allowbreak py}, \texttt{02\_\allowbreak collaboration.\allowbreak py}, \texttt{03\_\allowbreak citation\_\allowbreak impact.\allowbreak py}, \texttt{knowledge\_\allowbreak graph/\allowbreak 01\_\allowbreak citation\_\allowbreak graph.\allowbreak py} & step 10 & \texttt{results/\allowbreak bibliometrics/\allowbreak *.\allowbreak csv}, \texttt{results/\allowbreak graph/\allowbreak *}, \texttt{data/\allowbreak processed/\allowbreak graph/\allowbreak *} & minutes \\11912 & Classification: \texttt{python taxonomy/\allowbreak classify.\allowbreak py --root \ldots --ontology taxonomy/\allowbreak ontology.\allowbreak yaml --prepare --run} (in practice \texttt{taxonomy/\allowbreak run\_\allowbreak classify\_\allowbreak shards.\allowbreak sh}, see incident I-3) & steps 8, 10; \texttt{ontology.\allowbreak yaml} & \texttt{classification/\allowbreak cats/\allowbreak *.\allowbreak parquet}, \texttt{classification/\allowbreak primary/\allowbreak *.\allowbreak parquet}, report & to be measured \\12013 & Validation sample and raters: \texttt{python taxonomy/\allowbreak validate\_\allowbreak sample.\allowbreak py --root \ldots --draw}; \texttt{--rate --model claude-haiku-5-5}; \texttt{--rate --model claude-sonnet-5-5}; \texttt{--metrics} & step 12 & \texttt{taxonomy/\allowbreak validation/\allowbreak *} & to be measured \\12114 & Embeddings: \texttt{python embeddings/\allowbreak embed\_\allowbreak corpus.\allowbreak py --root \ldots --prepare}; \texttt{--run --model allenai/\allowbreak specter2\_\allowbreak base} (sharded over nodes) & step 10 & \texttt{embeddings/\allowbreak <model>/\allowbreak shard\_\allowbreak *.\allowbreak npy} & $\approx$300 texts/s per M3~Ultra \\12215 & Structural change, emerging detection, figures, tables: \texttt{econometrics/\allowbreak run\_\allowbreak structural\_\allowbreak change.\allowbreak py}, \texttt{experiments/\allowbreak emerging\_\allowbreak detection.\allowbreak py}, \texttt{figures/\allowbreak make\_\allowbreak figures.\allowbreak py}, \texttt{tables/\allowbreak make\_\allowbreak numbers.\allowbreak py} & steps 11--14 & \texttt{results/\allowbreak econometrics}, \texttt{results/\allowbreak emerging}, \texttt{figures/\allowbreak }, \texttt{manuscript/\allowbreak tables/\allowbreak numbers.\allowbreak tex} & minutes \\12316 & Manuscript: \texttt{cd manuscript \&\& make} (\texttt{latexmk}) & all & \texttt{manuscript/\allowbreak main.\allowbreak pdf} & --- \\124\end{longtable}125\end{footnotesize}126\end{singlespace}127128The test suite (\texttt{python -m pytest -q tests/\allowbreak }) covers the structural-change toolkit and the129classification scoring rule (\texttt{tests/\allowbreak test\_\allowbreak structural\_\allowbreak change.\allowbreak py}, \texttt{tests/\allowbreak test\_\allowbreak classify\_\allowbreak rules.\allowbreak py});130the second fixes the implemented aggregation of equation~\eqref{eq:score} against the toy ontology it131declares, so that a change in \texttt{classify.\allowbreak py} that altered the rule would fail before any corpus were132classified.133134\subsection{Provenance mechanisms}135\label{app:repro-provenance}136137Provenance is kept at the level of files and jobs, not of individual records, by six mechanisms.138139\paragraph{Manifests and hash ledgers for the OpenAlex snapshot.} The snapshot's own manifest140(\texttt{works\_\allowbreak manifest\_\allowbreak 2026-10-14.\allowbreak json}) lists every part file with its content length and record count.141The ingestion script keeps a SQLite ledger (\texttt{works/\allowbreak ledger.\allowbreak sqlite}) with a \texttt{files} table (one142row per part: URL, local name, expected length, manifest record count, status, SHA-256 of the gzip bytes,143records parsed, records retained by the filter, lines that failed to parse, download and total time, number144of attempts, start and end timestamps, error text) and a \texttt{runs} table (start time, manifest date,145manifest record and byte totals, number of workers, host name and command line of every invocation). A146part is downloaded with resume and retries until its size equals the manifest's, hashed, parsed, and only147then marked done; outputs are written to a temporary name and renamed atomically, so an interrupted run148leaves no partial output that a resumed run would mistake for a complete one. The raw gzip files are deleted149after a successful parse unless \texttt{--keep-raw} is given: the snapshot is a public, versioned release, and150the manifest together with the per-file hashes allows any later copy to be verified against what was151ingested. For the first pass the ledger records 1{,}898 of 1{,}898 files done, 0 parse errors, and a sum of152per-file record counts equal to the manifest total.153154\paragraph{Entity ledger.} The small entity snapshots (topics, subfields, fields, domains, sources,155institutions, publishers, funders, concepts) are listed in \texttt{entities\_\allowbreak ledger.\allowbreak json} with, per file, the156entity, the local path, the byte count, the SHA-256 and the status.157158\paragraph{DBLP checksum.} The DBLP dump is distributed with an \texttt{.md5} file; the downloaded159\texttt{dblp.\allowbreak xml.\allowbreak gz} was verified against it (\texttt{ff1c424c3b4cef36582f647c36df27c0}). The parse report160(\texttt{dblp\_\allowbreak parse\_\allowbreak report.\allowbreak json}) additionally records the input path, its size in bytes, an MD5 of its161first GiB (the file is 1.12~GB, so this digest is not the published one and is a convenience for detecting a162changed input), the parse timestamp, per-type and per-year record counts, and timings.163164\paragraph{Verbatim OAI-PMH pages.} The arXiv harvest stores every XML response page as received under165\texttt{data/\allowbreak raw/\allowbreak arxiv/\allowbreak raw/\allowbreak <window>/\allowbreak page\_\allowbreak NNNN.\allowbreak xml} (758 pages for the monthly windows from 2018-01 to1662026-11), together with a harvest state file that allows resumption by window and a parse report; the167Parquet table can be rebuilt from the pages alone with \texttt{--parse-only}. Because OAI selects by record168\emph{datestamp}, a record updated in several windows appears in several pages; the parser keeps the latest.169170\paragraph{API ledger.} The OpenAlex API increment (works with a CS topic published between 2026-10-01 and1712026-11-02, fetched after the snapshot) stores every JSON page verbatim under \texttt{api\_\allowbreak increment/\allowbreak pages/\allowbreak }172and a ledger (\texttt{api\_\allowbreak increment/\allowbreak ledger.\allowbreak json}) with the slices requested (day $\times$ CS-primary or not),173the number of calls and the cost charged against the public budget; without an API key the harvest is paged174by day with at most 10{,}000 results per slice and spread over several days.175176\paragraph{Step reports and generated numbers.} Each processing step writes a machine-readable report:177\texttt{linkage\_\allowbreak report.\allowbreak json} (counts of keys, links by method, unlinked records by type, identifiers needed178for the second pass), \texttt{prisma\_\allowbreak counts.\allowbreak json} (every PRISMA stage, with an identity check that the179records loaded equal the sum of sequential exclusions and the remainder), \texttt{classification\_\allowbreak report.\allowbreak json}180(ontology version, works scored, works classified, timestamp), one embedding manifest per host (model,181maximum sequence length, device, and per shard the number of texts, the seconds taken and the timestamp),182and the validation metrics. The macros of \texttt{manuscript/\allowbreak tables/\allowbreak numbers.\allowbreak tex} are generated from these183files by \texttt{tables/\allowbreak make\_\allowbreak numbers.\allowbreak py}; a number whose source file is missing yields an undefined macro184and therefore a compilation error, which is intended: the prose cannot cite a number that no result file185contains.186187\subsection{Incident log and its implications}188\label{app:repro-incidents}189190Six incidents were recorded during the first run (\texttt{reports/\allowbreak INCIDENTS.\allowbreak md}); none caused data loss,191because every step is resumable from its ledger or its shard files, but three of them changed how the192pipeline must be run and are therefore part of the reproduction instructions.193194\begin{table}[htbp]195\begin{singlespace}196\centering197\caption{Incident log of the first run (\texttt{reports/\allowbreak INCIDENTS.\allowbreak md}; times in Eastern Time, 2026-11-03).}198\label{tab:appH-incidents}199\footnotesize200\begin{tabularx}{\textwidth}{l l X X}201\toprule202\# & When & What happened & Impact and recovery \\203\midrule204I-1 & $\approx$15:45 & Second snapshot pass with \texttt{--id-list}: worker processes had an empty identifier set, because the macOS \texttt{spawn} start method does not inherit module globals & 68 part files marked in error, no data loss; a pool initialiser now loads the identifier set in each worker; ledger reset and rerun \\205I-2 & $\approx$15:50 & DuckDB ``too many open files'' when reading 1{,}898 Parquet files per table & Linkage step failed; \texttt{compact\_\allowbreak tables.\allowbreak py} (one file per table) and \texttt{ulimit -n 65536} \\206I-3 & $\approx$16:30 & \texttt{multiprocessing.\allowbreak Pool} with pyarrow in spawned workers hung (0\,\% CPU) in the classifier & Test stalled 10~min; shard mode: independent processes started by \texttt{run\_\allowbreak classify\_\allowbreak shards.\allowbreak sh} \\207I-4 & $\approx$16:58 & M3U96b froze (no ping, SSH ``host is down'') while running the second pass (8 download-and-parse workers), the SPECTER2 embedding job on the GPU and earlier DuckDB jobs concurrently & Second pass ($\approx$1{,}400 of 1{,}898 files done), the chained corpus v2 and classification, and the node's embedding share interrupted; other embedding nodes unaffected; all jobs resumable; rule adopted: never run ingestion, embeddings and classification concurrently on one node \\208I-5 & 17:16 & M3U96b froze again two minutes after the second pass was restarted alone on the rebooted node & Pass~2 moved to the idle node M5M36 (10 workers, ledger copied); M3U96b reserved for DuckDB and classification; the chain now waits for a flag raised when the pass-2 outputs are copied back; Apple diagnostics show performance auto-bug-capture reports, no kernel panic found \\209I-6 & 17:17 & M4M64a froze a second time within two minutes of restarting its embedding share (batch 32); M3U96b rebooted in the same minute while running pass~2 alone & M4M64a embedding share lost again; M4M64a and M3U96b withdrawn from GPU and bulk-download work; shard ranges reassigned to M4M64c and M3U96a; pass~2 on M5M36 \\210\bottomrule211\end{tabularx}212\end{singlespace}213\end{table}214215Three implications follow for anyone reproducing the work. First, the parallelism in the scripts assumes216the \texttt{spawn} start method of macOS: worker state must be built in initialisers, and the classifier217should be run as independent shard processes rather than through a pool (\texttt{classify.\allowbreak py --shard i/\allowbreak N});218on Linux, where \texttt{fork} is the default, the pool mode may work but has not been exercised. Second, the219per-part Parquet layout exceeds default file-descriptor limits; the compaction step is mandatory, not an220optimisation. Third, the two freezes of the primary node occurred under concurrent download, parse, GPU221and database load, and their cause was not established from the system logs; the operational rule adopted222afterwards (one heavy job per node at a time) costs wall time but is the configuration under which the223reported outputs were produced, and the measured times of Table~\ref{tab:appH-steps} assume it. Because224every job writes its outputs atomically and records what it has completed, the state of the pipeline after225an interruption is recoverable from the ledgers alone, and the outputs of a resumed run are identical to226those of an uninterrupted one for all deterministic steps (ingestion, normalisation, linkage, corpus227definition, classification). The embedding step is the exception: shards were computed on several nodes and228devices, and the per-host manifests record for each shard where it was computed, so that any numerical229difference between devices can be traced; the embeddings are stored in half precision and are not claimed230to be bit-identical across hardware.231232\subsection{Protocol amendments}233\label{app:repro-amendments}234235The protocol was frozen on 2026-11-02 before any analysis of the corpus, and every later change is an236amendment with an identifier, a reason and an approver (\texttt{protocol/\allowbreak amendments.\allowbreak csv}). All seven amendments (A-000 to A-006)237of Table~\ref{tab:appH-amendments} are approved by the executing agent and pending the principal238investigator's signature at the checkpoint of the corresponding phase; the manuscript marks the affected239conclusions accordingly.240241\begin{table}[htbp]242\begin{singlespace}243\centering244\caption{Protocol amendments (\texttt{protocol/\allowbreak amendments.\allowbreak csv}).}245\label{tab:appH-amendments}246\footnotesize247\begin{tabularx}{\textwidth}{l l l X X}248\toprule249Id & Date & Phase & Change & Reason \\250\midrule251A-000 & 2026-11-02 & 0 & \S14 of the mandate completed from \S1--\S13 & The pasted mandate was truncated by the terminal \\252A-001 & 2026-11-03 & 1 & Eligibility type list corrected: conference-paper and software-paper are kept; DBLP-linked records are kept whatever their type unless hard-excluded; XPAC records kept when DBLP-linked (\texttt{xpac\_\allowbreak kept\_\allowbreak dblp}) & The first run excluded 2.5~M conference-paper records (1.09~M DBLP-linked) through an outdated type list \\253A-002 & 2026-11-03 & 1 & Records whose primary source is a generic deposit platform are excluded unless DBLP- or arXiv-linked (E7); \texttt{date\_\allowbreak precision} added, monthly series use month-precision records only & Zenodo alone contributed 138{,}533 otherwise eligible 2026 records (deposits of slides, datasets and reports typed as articles); 18--28\,\% of records carry a year-only date \\254A-003 & 2026-11-03 & 2 & LLM rating budget for the taxonomy validation fixed at 25~USD (two raters, structured outputs) & Mandate \S7 requires agent-based validation with human checkpoints; the PI rates 200 works \\255A-004 & 2026-11-03 & 3 & Knowledge-graph extraction from abstracts of the high-priority set with \texttt{claude-sonnet-5-5}, provenance spans and machine verification of spans; budget 150~USD & Mandate \S6.4 requires provenance, confidence and verification status on every inferred relationship \\256A-005 & 2026-11-03 & 1 & Exclusion code E7 redefined as the generic-deposit exclusion; the CS penumbra is a membership flag, not an exclusion code & The Part~IV writer found the protocol and code labels inconsistent \\257\bottomrule258\end{tabularx}259\end{singlespace}260\end{table}261262\subsection{Sources, licences and what is redistributed}263\label{app:repro-licences}264265Table~\ref{tab:appH-raw} lists each raw source as obtained, with its size, integrity record and licence.266Nothing under \texttt{data/\allowbreak raw/\allowbreak } or \texttt{data/\allowbreak processed/\allowbreak } is committed to the repository267(\texttt{.gitignore} excludes both, together with every Parquet and JSONL file, except the provenance268\texttt{README} and the metadata directory); the copies live on the compute node. What the released269repository contains is the code, the ontology and its audit table, the manifests, ledgers and hashes, the270PRISMA counts and the linkage report, the aggregated result tables, the figures, the generated number macros271and the manuscript (protocol \S P8). Record-level derived tables may be released where the source licence272permits: OpenAlex is CC0, DBLP is ODC-BY~1.0, arXiv metadata obtained through OAI-PMH is used under the arXiv273metadata terms of use (Part~III, Table~\ref{tab:data-sources}), which release the metadata itself under CC0,274and the ACL Anthology is CC~BY~4.0; a derived record table therefore carries the275attribution required by ODC-BY and CC~BY for the DBLP and ACL fields it contains. Full texts are never276redistributed and were never obtained through paywalls. The Semantic Scholar Academic Graph was not used:277bulk access requires an API key that was not available and anonymous calls were refused, and the mandate's278instruction to record inaccessible data explicitly is satisfied by this note; influential-citation and279citation-intent signals derived from that graph are consequently absent from the analyses.280281\begin{table}[htbp]282\begin{singlespace}283\centering284\caption{Raw sources as obtained (\texttt{data/\allowbreak raw/\allowbreak README.\allowbreak md}), with integrity record and licence.}285\label{tab:appH-raw}286\footnotesize287\begin{tabularx}{\textwidth}{p{2.6cm} X p{3.6cm} p{2.0cm}}288\toprule289Source & What was obtained, when, size & Integrity & Licence \\290\midrule291OpenAlex works snapshot, release 2026-10-14 & \texttt{data/\allowbreak jsonl/\allowbreak works/\allowbreak }: 1{,}898 part files, \LinkSlimTotal{} works, 675.3~GB gzip; streamed 2026-11-02 21:19--23:05~ET over HTTPS with 8 parallel streams; retained: CS subset 20~GB (\PrismaIdentifiedOpenalexPassOneCsTopicFilter{} records) and slim table 9.3~GB (all works) & manifest; per-file content length and SHA-256 in \texttt{works/\allowbreak ledger.\allowbreak sqlite} (1{,}898/1{,}898 ok, 0 parse errors, record total = manifest total) & CC0 \\292OpenAlex entity snapshots & topics 4{,}516; subfields 252; fields 26; domains 4; sources 268{,}629; institutions 143{,}455; publishers 10{,}713; funders 41{,}799; concepts 65{,}026; 2026-11-02; 0.6~GB & \texttt{entities\_\allowbreak ledger.\allowbreak json} (SHA-256 per file) & CC0 \\293OpenAlex API increment & works with any CS topic, publication date 2026-10-01 to 2026-11-02, sliced by day and CS-primary status, basic paging $\le$10{,}000 per slice; from 2026-11-03 (public budget 0.10~USD/day, several days) & raw JSON pages; \texttt{api\_\allowbreak increment/\allowbreak ledger.\allowbreak json} (counts, cost) & CC0 \\294DBLP & \texttt{dblp.\allowbreak xml.\allowbreak gz} (2026-11-02 22:39~UTC), \texttt{dblp.\allowbreak dtd}, \texttt{.md5}; 1.12~GB & MD5 \texttt{ff1c424c\ldots27c0} verified; parse report (\DblpParsedRecords{} records) & ODC-BY 1.0 \\295arXiv OAI-PMH & set \texttt{cs}, metadata format \texttt{arXiv}, datestamps 2018-01-01 to 2026-11-02 in monthly windows, raw XML pages kept; 2026-11-02/03 & 758 pages, \LinkArxivCsRecords{} unique records; \texttt{harvest\_\allowbreak state.\allowbreak json}, parse report & arXiv metadata terms (CC0) \\296ACL Anthology & \texttt{anthology+abstracts.\allowbreak bib.\allowbreak gz}; 2026-11-02; 46.7~MB, \AclParsedEntries{} entries & SHA-256 \texttt{44c3f7e9\ldots58e3} & CC BY 4.0 \\297\bottomrule298\end{tabularx}299\end{singlespace}300\end{table}301302\subsection{Minimal reproduction sequence}303\label{app:repro-minimal}304305A reader with a macOS or Linux machine, disk for the retained outputs (the CS subset and the slim table306occupy about 30~GB; the 675.3~GB of gzip parts are streamed and deleted after parsing unless307\texttt{--keep-raw} is given), memory for the DuckDB steps as configured (the scripts set308\texttt{memory\_\allowbreak limit} to 60--70~GB, to be lowered on smaller machines at the cost of time) and no309credentials can reproduce the corpus and the310bibliometric tables by creating the environment (\texttt{uv venv --python 3.\allowbreak 12 .\allowbreak venv \&\& uv pip install -r311requirements.\allowbreak txt}), downloading the manifest of the 2026-10-14 release, and running steps~1--11 of312Table~\ref{tab:appH-steps} in order, with the second pass of step~9 between steps~8 and~10. The snapshot313release is versioned, so a later release will not reproduce the counts; the manifest date is the parameter314that pins them. Steps~12 and~14 need no credentials but benefit from several nodes; step~13 and the315knowledge-graph extraction of amendment A-004 need an Anthropic API key and a budget of 25 and 150~USD316respectively, and their outputs (ratings, extracted edges) are released so that the downstream statistics317can be recomputed without re-rating. Step~15 regenerates every figure, table and number macro from the318result files, and step~16 compiles the manuscript; a missing result file surfaces as an undefined macro at319compilation, which is the intended way of detecting that a number has no source.320
Files · 0cf4d9783f4f93dcc7f36ef98968e60574da3fd5
Menu

Theme