adduce 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. adduce-0.1.0/.gitignore +49 -0
  2. adduce-0.1.0/CHANGELOG.md +83 -0
  3. adduce-0.1.0/LICENSE +21 -0
  4. adduce-0.1.0/PKG-INFO +301 -0
  5. adduce-0.1.0/README.md +240 -0
  6. adduce-0.1.0/corpus/README.md +46 -0
  7. adduce-0.1.0/docs/rules/README.md +82 -0
  8. adduce-0.1.0/pyproject.toml +93 -0
  9. adduce-0.1.0/src/adduce/__init__.py +3 -0
  10. adduce-0.1.0/src/adduce/cache.py +44 -0
  11. adduce-0.1.0/src/adduce/checklists/__init__.py +149 -0
  12. adduce-0.1.0/src/adduce/checklists/acl.yaml +44 -0
  13. adduce-0.1.0/src/adduce/checklists/neurips.yaml +44 -0
  14. adduce-0.1.0/src/adduce/cli.py +715 -0
  15. adduce-0.1.0/src/adduce/config.py +54 -0
  16. adduce-0.1.0/src/adduce/dynamic/__init__.py +7 -0
  17. adduce-0.1.0/src/adduce/dynamic/import_hook.py +111 -0
  18. adduce-0.1.0/src/adduce/dynamic/reproduce.py +169 -0
  19. adduce-0.1.0/src/adduce/dynamic/resolve.py +98 -0
  20. adduce-0.1.0/src/adduce/engine.py +134 -0
  21. adduce-0.1.0/src/adduce/evidence/__init__.py +117 -0
  22. adduce-0.1.0/src/adduce/evidence/config.py +141 -0
  23. adduce-0.1.0/src/adduce/evidence/data.py +127 -0
  24. adduce-0.1.0/src/adduce/evidence/dependencies.py +227 -0
  25. adduce-0.1.0/src/adduce/evidence/docs.py +116 -0
  26. adduce-0.1.0/src/adduce/evidence/environment.py +75 -0
  27. adduce-0.1.0/src/adduce/evidence/git.py +32 -0
  28. adduce-0.1.0/src/adduce/evidence/latex.py +280 -0
  29. adduce-0.1.0/src/adduce/evidence/notebook.py +127 -0
  30. adduce-0.1.0/src/adduce/evidence/portability.py +73 -0
  31. adduce-0.1.0/src/adduce/evidence/precision.py +118 -0
  32. adduce-0.1.0/src/adduce/evidence/python_ast.py +611 -0
  33. adduce-0.1.0/src/adduce/evidence/remote.py +179 -0
  34. adduce-0.1.0/src/adduce/evidence/results.py +146 -0
  35. adduce-0.1.0/src/adduce/evidence/run_history.py +179 -0
  36. adduce-0.1.0/src/adduce/fixers/__init__.py +157 -0
  37. adduce-0.1.0/src/adduce/fixers/codemods/__init__.py +1 -0
  38. adduce-0.1.0/src/adduce/fixers/codemods/pin_revision.py +75 -0
  39. adduce-0.1.0/src/adduce/fixers/templates/CITATION.cff.j2 +13 -0
  40. adduce-0.1.0/src/adduce/fixers/templates/Dockerfile.j2 +19 -0
  41. adduce-0.1.0/src/adduce/fixers/templates/readme_sections.md.j2 +37 -0
  42. adduce-0.1.0/src/adduce/fixers/templates/reproduce.sh.j2 +17 -0
  43. adduce-0.1.0/src/adduce/fixers/templates/seed_utils.py.j2 +59 -0
  44. adduce-0.1.0/src/adduce/graph.py +164 -0
  45. adduce-0.1.0/src/adduce/llm/__init__.py +119 -0
  46. adduce-0.1.0/src/adduce/manifest.py +256 -0
  47. adduce-0.1.0/src/adduce/manifest_builder.py +132 -0
  48. adduce-0.1.0/src/adduce/model.py +233 -0
  49. adduce-0.1.0/src/adduce/modes.py +111 -0
  50. adduce-0.1.0/src/adduce/naming/__init__.py +139 -0
  51. adduce-0.1.0/src/adduce/profiles/__init__.py +82 -0
  52. adduce-0.1.0/src/adduce/profiles/acl.toml +21 -0
  53. adduce-0.1.0/src/adduce/profiles/acm.toml +21 -0
  54. adduce-0.1.0/src/adduce/profiles/default.toml +21 -0
  55. adduce-0.1.0/src/adduce/profiles/iclr.toml +21 -0
  56. adduce-0.1.0/src/adduce/profiles/neurips.toml +21 -0
  57. adduce-0.1.0/src/adduce/profiles/strict.toml +21 -0
  58. adduce-0.1.0/src/adduce/report/__init__.py +34 -0
  59. adduce-0.1.0/src/adduce/report/appendix.py +101 -0
  60. adduce-0.1.0/src/adduce/report/badge.py +70 -0
  61. adduce-0.1.0/src/adduce/report/checksums.py +38 -0
  62. adduce-0.1.0/src/adduce/report/codemeta.py +29 -0
  63. adduce-0.1.0/src/adduce/report/croissant.py +55 -0
  64. adduce-0.1.0/src/adduce/report/json_report.py +48 -0
  65. adduce-0.1.0/src/adduce/report/latex.py +58 -0
  66. adduce-0.1.0/src/adduce/report/markdown.py +61 -0
  67. adduce-0.1.0/src/adduce/report/ro_crate.py +69 -0
  68. adduce-0.1.0/src/adduce/report/sarif.py +91 -0
  69. adduce-0.1.0/src/adduce/report/software_heritage.py +36 -0
  70. adduce-0.1.0/src/adduce/report/terminal.py +212 -0
  71. adduce-0.1.0/src/adduce/report/zenodo.py +24 -0
  72. adduce-0.1.0/src/adduce/reviewer_time.py +90 -0
  73. adduce-0.1.0/src/adduce/rules/__init__.py +4 -0
  74. adduce-0.1.0/src/adduce/rules/archival.py +97 -0
  75. adduce-0.1.0/src/adduce/rules/base.py +135 -0
  76. adduce-0.1.0/src/adduce/rules/builtin.py +5 -0
  77. adduce-0.1.0/src/adduce/rules/checkpoint.py +130 -0
  78. adduce-0.1.0/src/adduce/rules/data.py +245 -0
  79. adduce-0.1.0/src/adduce/rules/deps.py +262 -0
  80. adduce-0.1.0/src/adduce/rules/determinism.py +321 -0
  81. adduce-0.1.0/src/adduce/rules/docs.py +128 -0
  82. adduce-0.1.0/src/adduce/rules/drift.py +334 -0
  83. adduce-0.1.0/src/adduce/rules/env.py +198 -0
  84. adduce-0.1.0/src/adduce/rules/exec_.py +140 -0
  85. adduce-0.1.0/src/adduce/rules/licensing.py +91 -0
  86. adduce-0.1.0/src/adduce/rules/notebook.py +220 -0
  87. adduce-0.1.0/src/adduce/rules/portability.py +108 -0
  88. adduce-0.1.0/src/adduce/rules/precision.py +180 -0
  89. adduce-0.1.0/src/adduce/rules/reconcile.py +197 -0
  90. adduce-0.1.0/src/adduce/rules/registry.py +218 -0
  91. adduce-0.1.0/src/adduce/rules/remote.py +165 -0
  92. adduce-0.1.0/src/adduce/rules/run.py +150 -0
  93. adduce-0.1.0/src/adduce/rules/versioning.py +77 -0
  94. adduce-0.1.0/src/adduce/scoring.py +136 -0
  95. adduce-0.1.0/tests/__init__.py +0 -0
  96. adduce-0.1.0/tests/conftest.py +45 -0
  97. adduce-0.1.0/tests/test_checklists.py +49 -0
  98. adduce-0.1.0/tests/test_cli.py +137 -0
  99. adduce-0.1.0/tests/test_cli_new.py +208 -0
  100. adduce-0.1.0/tests/test_collectors_new.py +211 -0
  101. adduce-0.1.0/tests/test_dependencies.py +110 -0
  102. adduce-0.1.0/tests/test_engine.py +175 -0
  103. adduce-0.1.0/tests/test_manifest_graph.py +144 -0
  104. adduce-0.1.0/tests/test_python_ast.py +156 -0
  105. adduce-0.1.0/tests/test_reporters.py +70 -0
  106. adduce-0.1.0/tests/test_rules_determinism.py +169 -0
  107. adduce-0.1.0/tests/test_rules_misc.py +141 -0
  108. adduce-0.1.0/tests/test_rules_new.py +249 -0
  109. adduce-0.1.0/tests/test_scoring.py +86 -0
@@ -0,0 +1,49 @@
1
+ # Byte-compiled / optimized
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ wheels/
12
+ MANIFEST
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+ .python-version.local
19
+
20
+ # Test / coverage
21
+ .pytest_cache/
22
+ .coverage
23
+ .coverage.*
24
+ coverage.xml
25
+ htmlcov/
26
+ .tox/
27
+ .nox/
28
+
29
+ # Type checking / linting caches
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+ .dmypy.json
33
+
34
+ # Editors and IDEs
35
+ .idea/
36
+ .vscode/
37
+ *.swp
38
+ *.swo
39
+ *~
40
+
41
+ # OS artifacts
42
+ .DS_Store
43
+ Thumbs.db
44
+
45
+ # Local tool state (the manifest and baseline under .adduce/ are meant to be
46
+ # committed; the cache and reproduce reports are machine-local)
47
+ .adduce/cache/
48
+ .adduce/reproduce-report.json
49
+
@@ -0,0 +1,83 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. The format
4
+ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
5
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - 2026-07-04
8
+
9
+ First release: a local research-artifact auditor. Offline by default;
10
+ online resolution and dynamic verification are separate, opt-in commands.
11
+
12
+ ### The static audit
13
+
14
+ - `adduce check`: 78 rules across 17 categories — code & execution,
15
+ environment, dependencies, data, documentation, determinism, numerical
16
+ precision & hardware, paper & artifact consistency (drift), result
17
+ reconciliation, run traceability, checkpoint state, notebooks,
18
+ portability, remote artifacts & rot, versioning, access & legal, and
19
+ archival readiness. Every finding carries status, confidence, locations,
20
+ and a remediation; framework gating keeps inapplicable rules out of the
21
+ score in both directions.
22
+ - Layered determinism analysis (per-library seeds, cuDNN flags, strict
23
+ controls, both DataLoader RNG sources, sklearn `random_state`), resolved
24
+ through an import-alias map plus one-hop wrapper resolution.
25
+ - Evidence collectors for Python AST, configs (YAML/JSON/TOML/Hydra/
26
+ DeepSpeed), LaTeX sources (comment stripping, scientific/LaTeX-math
27
+ notation, table parsing), notebooks, dependency manifests, data
28
+ provenance, remote-artifact calls, precision controls, result files
29
+ (CSV/JSON/JSONL, TensorBoard/W&B/MLflow presence), run history (shell,
30
+ Makefile, SLURM, Hydra outputs, W&B/MLflow metadata), portability, and git.
31
+
32
+ ### Claim traceability
33
+
34
+ - The Reproducibility Manifest (`.adduce/manifest.yaml`, `adduce manifest`):
35
+ claims, datasets, remotes, environment, and a smoke target; auto-drafted
36
+ from evidence, authoritative once confirmed.
37
+ - The claim-to-artifact graph: per-claim trails (metric → command → config →
38
+ data → env → seeds → commit) with per-edge resolution status, printed in
39
+ `adduce check` and exported in JSON.
40
+ - Paper↔code drift detection with authority ranking (materialised run config
41
+ over checked-in config over defaults) and rounding-aware comparison;
42
+ result reconciliation against local logs.
43
+ - Reviewer time-to-first-result estimation with named cost factors, and
44
+ three report framings: `--mode author|reviewer|ae-chair` (the last with
45
+ ACM badge-eligibility assessment; execution-based badges never claimed).
46
+
47
+ ### Deliverables
48
+
49
+ - `adduce checklist` (NeurIPS, ACL) and `adduce appendix` (ACM Artifact
50
+ Appendix), drafted from evidence with author-input items marked.
51
+ - `adduce export`: RO-Crate, Croissant (per dataset), CodeMeta,
52
+ `.zenodo.json`, `checksums.txt`, and a Software Heritage note;
53
+ `adduce archive-plan` for the deposit steps.
54
+ - Reports: Rich terminal, JSON, SARIF 2.1.0, Markdown, LaTeX appendix, and
55
+ a badge as shields.io endpoint JSON or self-contained SVG.
56
+ - Scaffolds (`adduce fix`): seed utilities, Dockerfile, CITATION.cff,
57
+ reproduce.sh, README sections — all non-destructive.
58
+
59
+ ### Fenced, opt-in layers
60
+
61
+ - `adduce pin-remotes`: offline detection of floating Hugging Face /
62
+ torch.hub / raw-URL references; opt-in online resolution of current SHAs
63
+ (cached in `.adduce/cache`) and diff-gated libcst codemods that add
64
+ `revision=` pins, with the forward-guarantee caveat stated.
65
+ - `adduce reproduce`: runs the manifest smoke target twice with a pinned
66
+ seed, fingerprints outputs and stdout metrics, and asserts agreement;
67
+ requires `--yes` and is never invoked by `check`. Plus a first-use RNG
68
+ ordering diagnostic (`python -m adduce.dynamic.import_hook`).
69
+ - Optional BYO-key LLM layer (OpenAI/Anthropic/Ollama) for checklist
70
+ justification prose only; checks and scoring stay deterministic.
71
+
72
+ ### Adoption machinery
73
+
74
+ - Explainable category-weighted scoring with venue profiles (`default`,
75
+ `neurips`, `iclr`, `acl`, `acm`, `strict`) and custom TOMLs.
76
+ - `adduce baseline` + `--fail-on-regression` ratchet; diagnostic-by-default
77
+ CI posture; `adduce diff` artifact-regression mode.
78
+ - Inline suppression (`# adduce: ignore=R-XXX-000`) and `[tool.adduce]`
79
+ configuration; `--only`/`--skip` rule filtering.
80
+ - Plugin entry points for rules (`adduce.rules`) and reporters
81
+ (`adduce.reporters`); composite GitHub Action and pre-commit hook.
82
+ - Validation-corpus harness (`corpus/run_validation.py`) implementing the
83
+ two-cohort protocol with honest measurement rules.
adduce-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harsh Chudasama
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
adduce-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: adduce
3
+ Version: 0.1.0
4
+ Summary: A local research-artifact auditor: checks that a paper's claims, code, configs, data, and results still agree, and produces the artifacts reviewers ask for.
5
+ Project-URL: Homepage, https://github.com/QHarshil/adduce
6
+ Project-URL: Repository, https://github.com/QHarshil/adduce
7
+ Project-URL: Issues, https://github.com/QHarshil/adduce/issues
8
+ Project-URL: Changelog, https://github.com/QHarshil/adduce/blob/main/CHANGELOG.md
9
+ Author-email: Harsh Chudasama <chudasama.h@northeastern.edu>
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 Harsh Chudasama
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: artifact-evaluation,auditing,machine-learning,provenance,reproducibility,research,static-analysis
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Environment :: Console
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Intended Audience :: Science/Research
37
+ Classifier: License :: OSI Approved :: MIT License
38
+ Classifier: Operating System :: OS Independent
39
+ Classifier: Programming Language :: Python :: 3
40
+ Classifier: Programming Language :: Python :: 3.10
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3.12
43
+ Classifier: Programming Language :: Python :: 3.13
44
+ Classifier: Programming Language :: Python :: 3.14
45
+ Classifier: Topic :: Scientific/Engineering
46
+ Classifier: Topic :: Software Development :: Quality Assurance
47
+ Requires-Python: >=3.10
48
+ Requires-Dist: jinja2>=3.1
49
+ Requires-Dist: libcst>=1.1
50
+ Requires-Dist: pyyaml>=6.0
51
+ Requires-Dist: rich>=13.0
52
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
53
+ Requires-Dist: typer>=0.12
54
+ Provides-Extra: dev
55
+ Requires-Dist: mypy>=1.10; extra == 'dev'
56
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
57
+ Requires-Dist: pytest>=8.0; extra == 'dev'
58
+ Requires-Dist: ruff>=0.6; extra == 'dev'
59
+ Requires-Dist: types-pyyaml; extra == 'dev'
60
+ Description-Content-Type: text/markdown
61
+
62
+ # adduce
63
+
64
+ **A local research-artifact auditor.**
65
+
66
+ `adduce` checks whether a paper's claims, code, configs, data, dependencies, remote models, precision settings, and generated results still agree with each other before submission — and produces the artifacts reviewers and conferences ask for: filled NeurIPS/ACL checklists, an ACM Artifact Appendix, archival metadata (RO-Crate, Croissant, CodeMeta, Zenodo), and a claim-by-claim evidence trail.
67
+
68
+ ```
69
+ pipx install adduce # or: pip install adduce / uvx adduce
70
+ adduce check .
71
+ ```
72
+
73
+ The north-star question: *for every number in the paper, can I point to the artifact that produced it, and will that artifact still produce it elsewhere?*
74
+
75
+ > `adduce` is offline by default. It never sends repository contents anywhere. Online checks are opt-in (`--online` or the `pin-remotes`/`archive-plan` commands) and only resolve public remote metadata such as Hugging Face model and dataset revisions, GitHub release SHAs, and URL headers. Resolved values are cached in `.adduce/cache` and written to the manifest only when requested. No server is operated by the project; all requests originate from the user's machine.
76
+
77
+ ## What it reports
78
+
79
+ ```
80
+ ╭─ adduce · cinematch · commit 3f9a1c2 ─────────────────────────────────────╮
81
+ │ Reproducibility 78/100 Silver · profile: default │
82
+ ╰────────────────────────────────────────────────────────────────────────────────╯
83
+ Reviewer time to first result: 45–90 min (Risky)
84
+ - no one-command reproduction path
85
+ - expected runtime not documented
86
+
87
+ Category Score Notes
88
+ Code & Execution 10/12 commands documented; no run script
89
+ Determinism & Model 8/12 seeds set; DataLoader workers unseeded
90
+ Paper & Artifact Consist. 4/8 learning_rate: paper says 0.0001, configs/main.yaml has 0.001
91
+ ...
92
+
93
+ Claim trails (manifest)
94
+ Table 2 · "LambdaMART improves NDCG@10 to 0.814"
95
+ metric results/lambdamart_eval.csv (found: 0.8127) ~ rounding vs paper (0.814) ✓
96
+ command make eval-lambdamart
97
+ config configs/lambdamart.yaml ✓
98
+ data data/splits/ml-25m/test.json ✓
99
+ env uv.lock + Dockerfile ✓
100
+ seeds 42, 43, 44
101
+ status PARTIAL
102
+ ```
103
+
104
+ Every finding carries a status (`pass` / `partial` / `fail` / `not-applicable` / `unknown`), a confidence, file:line locations, and a concrete remediation. `partial` is the most common and most useful state.
105
+
106
+ ## The three layers, and which one this is
107
+
108
+ The reproducibility problem has three layers. **Sharing** (findable, licensed, citable) is owned by FAIR tools like `howfairis`. **Packaging** (capture and replay execution) is owned by ReproZip, DataLad, and repo2docker. **Traceability** — does each reported claim map to the exact code, config, data, seed, environment, command, and logged result that produced it — is the layer reviewers actually probe, and the layer `adduce` owns, folding the other two in as inputs.
109
+
110
+ ## The Reproducibility Manifest
111
+
112
+ `.adduce/manifest.yaml` is the machine-readable source of truth. `adduce manifest` drafts it from detected evidence — claims extracted from the paper, datasets from loaders, unpinned remotes, the environment — and the author confirms it. Every other command consumes it: manifest-declared links are authoritative, inferred links carry confidence.
113
+
114
+ ```yaml
115
+ schema: adduce/1
116
+ claims:
117
+ - id: C1
118
+ text: "LambdaMART achieves NDCG@10 of 0.814"
119
+ where: "Table 2"
120
+ metric: "ndcg@10"
121
+ value: 0.814
122
+ seeds: [42, 43, 44]
123
+ produced_by:
124
+ command: "make eval-lambdamart"
125
+ config: configs/lambdamart.yaml
126
+ log: results/lambdamart_eval.csv
127
+ smoke:
128
+ command: "python train.py --config configs/smoke.yaml"
129
+ max_runtime_minutes: 10
130
+ expected_outputs: ["results/smoke_metrics.json"]
131
+ ```
132
+
133
+ The `smoke` target is the biggest usability lever an artifact can have: it lets a reviewer verify the pipeline's shape in minutes instead of "download 200 GB and train for three days."
134
+
135
+ ## What it checks
136
+
137
+ 78 rules across 17 categories:
138
+
139
+ | Category | Prefix | Examples |
140
+ |---|---|---|
141
+ | Code & Execution | `R-EXEC` | entrypoint, one-command runner, exact reproduce command |
142
+ | Environment & Tooling | `R-ENV` | pinning posture, lockfile, container, Python version, CUDA capture |
143
+ | Dependencies | `R-DEP` | ghost imports, unused declarations, notebook-only imports, system tools |
144
+ | Data | `R-DATA` | provenance, download path, checksums, LFS, access-friction grade A–E |
145
+ | Documentation | `R-DOC` | README sections, hyperparameters recorded, expected results |
146
+ | Determinism & Model | `R-DET` | layered seeds, cuDNN flags, strict mode, both DataLoader RNG sources, `random_state` |
147
+ | Numerical Precision & Hardware | `R-PREC` | undocumented TF32/AMP/bf16, hardware baseline (warnings, never fails) |
148
+ | Paper & Artifact Consistency | `R-DRIFT` | paper hyperparameter vs authoritative config, dataset drift, ablation traces |
149
+ | Result Reconciliation | `R-RES` | reported vs logged metrics, rounding vs material gaps, single-run detection |
150
+ | Run Traceability | `R-RUN` | per-claim commands, materialised Hydra configs vs committed ones, SLURM requests |
151
+ | Checkpoint & Experiment State | `R-CKPT` | optimizer/scheduler/RNG state, epoch, config/commit provenance in checkpoints |
152
+ | Notebooks | `R-NB` | execution order, hidden state, `!pip install` cells, seed-before-draw, script twins |
153
+ | Portability | `R-PORT` | absolute paths, localhost, drive-link data sources, committed secrets |
154
+ | Remote Artifacts & Rot | `R-REMOTE` | unpinned `from_pretrained`, mutable revisions, `torch.hub`, checksum-less downloads |
155
+ | Versioning | `R-VER` | git, tags, commit referenced in docs |
156
+ | Access & Legal | `R-LIC` | LICENSE, CITATION.cff, third-party asset licenses |
157
+ | Archival Readiness | `R-ARC` | DOI/SWHID, archivable size, `.zenodo.json`/`codemeta.json` |
158
+
159
+ Drift resolution uses an explicit authority ranking: a materialised run config (Hydra output, W&B, MLflow) outranks a checked-in config, which outranks an argparse/dataclass default — a default alone is weak evidence of what actually ran. Floats compare with rounding-awareness (a paper's 0.814 matches a logged 0.8137); nothing ever auto-edits the `.tex`.
160
+
161
+ Call resolution goes through an import-alias map (`import torch as th` is handled) plus one hop of wrapper resolution: a project-local `set_seed()` that calls the primitives counts. Python's dynamism (`getattr`, dynamic import) cannot be resolved statically — which is exactly why findings carry a confidence, never a verdict.
162
+
163
+ ## Commands
164
+
165
+ ```bash
166
+ adduce check . # everything offline: report, claim trails, reviewer time
167
+ adduce check --mode reviewer # skeptical framing: what could not be verified
168
+ adduce check --mode ae-chair # badge eligibility, blocking issues, burden headline
169
+ adduce check -f json|sarif|markdown|badge|latex -o out
170
+ adduce drift # paper ↔ code/config consistency + result reconciliation
171
+ adduce precision # TF32/AMP/low-precision audit
172
+ adduce deps # ghost/unused/notebook dependency analysis
173
+ adduce manifest # scaffold/refresh .adduce/manifest.yaml
174
+ adduce checklist --profile neurips # filled reproducibility checklist (also: acl)
175
+ adduce appendix # ACM Artifact Appendix draft
176
+ adduce export ro-crate|croissant|codemeta|zenodo|checksums|software-heritage|all
177
+ adduce badge --svg # committed-in-repo badge; no hosted endpoint
178
+ adduce diff main...HEAD # artifact regression: code changed, docs/manifest did not?
179
+ adduce archive-plan # exact steps to a Zenodo DOI / Software Heritage SWHID
180
+ adduce baseline # snapshot for the CI ratchet
181
+ adduce rules · adduce explain R-DET-001
182
+ adduce fix --scaffold seeds|docker|citation|runner|readme
183
+
184
+ # opt-in, clearly fenced:
185
+ adduce pin-remotes --diff # resolve current HF/GitHub SHAs (online), show pin diffs
186
+ adduce reproduce --yes # run the smoke target twice, assert the runs agree (executes repo code)
187
+ ```
188
+
189
+ `adduce reproduce` is the empirical layer: two runs with a pinned seed, fingerprinted (output hashes, stdout metrics), compared. It executes repository code, so it demands `--yes`, is designed to run inside the repo's own container or CI, and is never invoked by `check`. A first-use ordering diagnostic (`python -m adduce.dynamic.import_hook train.py`) reports whether seeding precedes the first RNG draw.
190
+
191
+ `adduce pin-remotes` resolves current revisions and drafts `revision="<sha>"` edits as diffs (libcst codemods, applied only with `--write`). Pinning to the *current* SHA is a forward guarantee — it does not recover the version historically used, and the output says so.
192
+
193
+ ## Reviewer time to first result
194
+
195
+ The score reframed into the currency a PI feels: `< 10 min` Excellent · `10–30` Good · `30–90` Risky · `90+` High reviewer burden — with the factors named (no one-command path, manual data fetch, no smoke target, undocumented runtime), so the author knows exactly what buys time back.
196
+
197
+ ## Scoring, profiles, suppression
198
+
199
+ Scoring is category-weighted and explainable — each category reports earned/possible with the findings that moved it; inapplicable categories drop out and the rest renormalise, so a scikit-learn repository is never scored against CUDA flags. Profiles: `default`, `neurips`, `iclr`, `acl`, `acm`, `strict`, or your own TOML.
200
+
201
+ ```python
202
+ loader = DataLoader(ds, shuffle=True) # adduce: ignore=R-DET-004
203
+ ```
204
+
205
+ ```toml
206
+ [tool.adduce] # or adduce.toml
207
+ profile = "neurips"
208
+ ignore = ["R-ARC-001"]
209
+ exclude = ["third_party"]
210
+ ```
211
+
212
+ Suppressed findings still appear, marked as ignored.
213
+
214
+ ## Continuous integration
215
+
216
+ The default run is diagnostic: `adduce check` exits 0 regardless of score. Gate with `--fail-under N`, or adopt incrementally with `adduce baseline` + `--fail-on-regression`, which fails only when a rule gets *worse* than the committed `.adduce/baseline.json` — new rules are never regressions, so upgrading the tool never punishes you.
217
+
218
+ ```yaml
219
+ # .github/workflows/reproducibility.yml
220
+ name: reproducibility
221
+ on: [pull_request]
222
+ jobs:
223
+ adduce:
224
+ runs-on: ubuntu-latest
225
+ steps:
226
+ - uses: actions/checkout@v4
227
+ - uses: QHarshil/adduce@v1
228
+ with:
229
+ profile: neurips
230
+ report-file: adduce-report.md # lands in the job summary
231
+ sarif-file: adduce.sarif
232
+ - uses: github/codeql-action/upload-sarif@v3 # code-scanning alerts on public repos
233
+ with:
234
+ sarif_file: adduce.sarif
235
+ ```
236
+
237
+ A pre-commit hook ships as well (`id: adduce`).
238
+
239
+ ## Extending adduce
240
+
241
+ Rules and reporters are discovered through entry points — the flake8/pytest pattern. A lab rule pack is an ordinary package:
242
+
243
+ ```python
244
+ # my_lab_rules.py
245
+ from adduce.rules import Category, Rule, Status
246
+
247
+ class SlurmScriptRule(Rule):
248
+ id = "R-LAB-001"
249
+ category = Category.CODE_EXECUTION
250
+ title = "SLURM submission script present"
251
+ rationale = "Our cluster reproductions start from a submit script."
252
+ weight = 3
253
+
254
+ def evaluate(self, ev):
255
+ scripts = ev.repo.find("slurm/*.sh") + ev.repo.find("*.sbatch")
256
+ if scripts:
257
+ return self.finding(Status.PASS, 0.9, f"Found {scripts[0].path}.")
258
+ return self.finding(Status.FAIL, 0.8, "No SLURM script found.",
259
+ remediation="Add slurm/submit.sh for the main experiment.")
260
+
261
+ RULES = [SlurmScriptRule]
262
+ ```
263
+
264
+ ```toml
265
+ [project.entry-points."adduce.rules"]
266
+ my_lab = "my_lab_rules"
267
+ # reporters: [project.entry-points."adduce.reporters"] name = "module:render"
268
+ ```
269
+
270
+ Installing the pack is all it takes.
271
+
272
+ ## Optional LLM layer
273
+
274
+ Strictly separated from checks and scoring, which stay deterministic and offline. With a configured provider (`ADDUCE_LLM_PROVIDER=openai|anthropic|ollama`, bring your own key or a local model), `adduce checklist --llm` drafts the free-text justification prose from the deterministic evidence. Without one, everything works identically. adduce ships no key and never calls a paid API on your behalf.
275
+
276
+ ## Honest limits
277
+
278
+ - **Signals, never certification.** adduce reports what it detected and what it could not; it never says "your code is reproducible", and it never assesses execution-based badges (Results Reproduced/Replicated).
279
+ - **Static resolution has a ceiling.** Alias plus one-hop wrapper resolution covers the common shapes of real ML code; Python's dynamism is unresolvable and reported as confidence, not verdicts, with `adduce reproduce` as the escape hatch.
280
+ - **The probabilistic rules are diagnostic.** LaTeX numeric extraction, result reconciliation, notebook staleness, and ablation matching will sometimes miss or over-flag; they carry confidence and stay off the blocking path by default.
281
+ - **Remote pinning is a forward guarantee**, not recovery of the version historically used.
282
+ - **CUDA/cuDNN versions are rarely in source.** adduce checks whether anything *captures* them (container, conda env, manifest), not that it can read them from code.
283
+ - **Not a data-leakage detector.** Train/test contamination is undetectable statically and adduce claims nothing about it.
284
+ - **No hosted backend, ever.** The design is deliberately serverless so it stays free.
285
+
286
+ ## Development
287
+
288
+ ```bash
289
+ git clone https://github.com/QHarshil/adduce
290
+ cd adduce
291
+ python -m venv .venv && source .venv/bin/activate
292
+ pip install -e ".[dev]"
293
+ pytest
294
+ ruff check src tests
295
+ ```
296
+
297
+ Validation against real repositories is a standing quality gate — see [corpus/README.md](corpus/README.md) for the protocol and what may honestly be claimed from it. Contributions are welcome, especially false-positive reports: a check that cries wolf is a bug. See [CONTRIBUTING.md](CONTRIBUTING.md).
298
+
299
+ ## License
300
+
301
+ [MIT](LICENSE)