Replication package
===================

A Dead Link Is Not Lost Code: Separating Repository Reachability from Deposit
Availability in Zenodo Software Citations

Every figure in the manuscript is produced by these scripts from public APIs.
No credential is needed for any step, and no account is required.

Sources
-------
  Zenodo REST API        https://zenodo.org/api/records   (frame, sample, deposits)
  github.com             HTTP status of the repository URL only; no API token
  Crossref, DataCite     the reference list

Order
-----
  01_frame.py      what the index will let a sampler see: the deep-paging ceiling,
                   which date filter partitions the frame, and the stated rate limit
                                                        -> results/frame.json
  02_sample.py     the stratified draw. Ten pages per whole-year stratum, one per
                   month or ten-day slice, without replacement, each stratum seeded
                   by its own name                      -> results/sample.json
  03_resolve.py    instrument one: is the repository reachable? Three buckets -
                   ALIVE, GONE, and UNRESOLVED as its own denominator line
                                                        -> results/resolve.json
  04_anchors.py    the reference list, resolved from Crossref and DataCite and
                   rendered as APA 7                    -> research/anchors*.{json,txt,tex}
  05_deposit.py    instrument two: when the repository is gone, does the deposit
                   still serve its files? Census of the gone set plus a seeded
                   random control from the alive set    -> results/deposit.json
  06_analysis.py   the stratified ratio estimator, the design effect, Wilson
                   intervals and the trend test         -> results/analysis.json
  07_tables.py     the manuscript's tables, generated  -> draft/tables.tex
  10_check.py      the integrity gate: every printed value against results/
  13_adversarial.py  the reverse scan: every number in the built PDF must have a
                   provenance, or the run fails

Seeds
-----
20260817 throughout, recorded in sample.json and deposit.json. Each sampling
stratum additionally derives its own generator from that seed and its own name,
so a stratum's draw depends on the seed and on nothing else - including on how
many pages any other stratum took.

Pacing, and why it is in the code
---------------------------------
Zenodo allows 30 unauthenticated requests per minute and states that limit in the
body of its own refusal. 02_sample.py pauses 2.5 seconds between requests and
05_deposit.py pauses 2.2. The first draw ran at one request a second and had 25
strata refused; the script reported the refusals rather than treating a refused
stratum as an empty one, which is the only reason that hole was visible.

What re-running will and will not reproduce
-------------------------------------------
The sample is deterministic given the seed and the frame, but the frame grows:
Zenodo gains software records daily, so a later run draws from a larger
population and the stratum sizes will differ. The resolution outcomes are a
measurement of the live web on the date recorded in resolve.json and deposit.json
and are not expected to reproduce exactly - a repository alive today may be gone
next year, which is the paper's subject.
