research-diff 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 (31) hide show
  1. research_diff-0.1.0/.gitattributes +2 -0
  2. research_diff-0.1.0/.github/workflows/ci.yml +37 -0
  3. research_diff-0.1.0/.github/workflows/release.yml +41 -0
  4. research_diff-0.1.0/.gitignore +15 -0
  5. research_diff-0.1.0/LICENSE +21 -0
  6. research_diff-0.1.0/PKG-INFO +111 -0
  7. research_diff-0.1.0/README.md +88 -0
  8. research_diff-0.1.0/docs/research.svg +78 -0
  9. research_diff-0.1.0/examples/batch-size/configs/e1.yaml +10 -0
  10. research_diff-0.1.0/examples/batch-size/notes/ideas.md +15 -0
  11. research_diff-0.1.0/examples/batch-size/notes/results.md +7 -0
  12. research_diff-0.1.0/examples/batch-size/out/e1.json +1 -0
  13. research_diff-0.1.0/examples/batch-size/paper/main.tex +7 -0
  14. research_diff-0.1.0/pyproject.toml +40 -0
  15. research_diff-0.1.0/research-diff.toml +8 -0
  16. research_diff-0.1.0/src/research_diff/__init__.py +2 -0
  17. research_diff-0.1.0/src/research_diff/__main__.py +5 -0
  18. research_diff-0.1.0/src/research_diff/agent.py +141 -0
  19. research_diff-0.1.0/src/research_diff/cli.py +153 -0
  20. research_diff-0.1.0/src/research_diff/compare.py +76 -0
  21. research_diff-0.1.0/src/research_diff/config.py +61 -0
  22. research_diff-0.1.0/src/research_diff/diff.py +106 -0
  23. research_diff-0.1.0/src/research_diff/extract.py +327 -0
  24. research_diff-0.1.0/src/research_diff/graph.py +94 -0
  25. research_diff-0.1.0/src/research_diff/mcp_server.py +112 -0
  26. research_diff-0.1.0/src/research_diff/readme.py +38 -0
  27. research_diff-0.1.0/src/research_diff/suggest.py +154 -0
  28. research_diff-0.1.0/src/research_diff/trackers.py +103 -0
  29. research_diff-0.1.0/src/research_diff/watch.py +69 -0
  30. research_diff-0.1.0/tests/conftest.py +46 -0
  31. research_diff-0.1.0/tests/test_research_diff.py +153 -0
@@ -0,0 +1,2 @@
1
+ * text=auto eol=lf
2
+ *.png binary
@@ -0,0 +1,37 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest, windows-latest]
17
+ python: ["3.11", "3.12", "3.13"]
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python }}
24
+ - run: python -m pip install -e ".[dev]"
25
+ - run: python -m pytest -q
26
+
27
+ map:
28
+ # The README's research map is drawn from examples/: it must be current.
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.12"
35
+ - run: python -m pip install -e .
36
+ - name: The research map is current (if not, run research-diff readme and commit)
37
+ run: research-diff readme --check
@@ -0,0 +1,41 @@
1
+ name: release
2
+
3
+ # A tag vX.Y.Z publishes that version to PyPI through trusted publishing: no token is stored anywhere. PyPI accepts the
4
+ # upload only from this workflow, in this repository, in the `pypi` environment.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+ - name: The tag is the version in pyproject.toml
21
+ run: |
22
+ v=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
23
+ [ "v$v" = "$GITHUB_REF_NAME" ] || { echo "tag $GITHUB_REF_NAME, but pyproject.toml says $v"; exit 1; }
24
+ - run: python -m pip install build && python -m build
25
+ - uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist
29
+
30
+ publish:
31
+ needs: build
32
+ runs-on: ubuntu-latest
33
+ environment: pypi
34
+ permissions:
35
+ id-token: write
36
+ steps:
37
+ - uses: actions/download-artifact@v4
38
+ with:
39
+ name: dist
40
+ path: dist
41
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv*/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+ .coverage
13
+ htmlcov/
14
+ /out/
15
+ docs/diagrams/*.png
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 simplifiedmachine
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.
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.5
2
+ Name: research-diff
3
+ Version: 0.1.0
4
+ Summary: Maps your research from its notes, experiment configs, results and paper (questions, hypotheses, experiments, results, claims) and shows what each iteration changed. Built for exploring ideas with AI coding agents.
5
+ Project-URL: Homepage, https://github.com/simplifiedmachine/research-diff
6
+ Project-URL: Documentation, https://github.com/simplifiedmachine/research-diff/wiki
7
+ Author: simplifiedmachine
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai-agents,claude-code,experiment-tracking,hypotheses,mcp,reproducibility,research
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Requires-Python: >=3.11
16
+ Requires-Dist: system-diff==0.1.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: mcp>=1.10; extra == 'dev'
19
+ Requires-Dist: pytest>=8; extra == 'dev'
20
+ Provides-Extra: mcp
21
+ Requires-Dist: mcp>=1.10; extra == 'mcp'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # research-diff
25
+
26
+ Maps your research from its notes, experiment configs, results and paper (questions, hypotheses, experiments,
27
+ results, claims) and shows what each iteration changed. Built for exploring ideas with AI coding agents.
28
+
29
+ <!-- research-map:begin -->
30
+ ![Research map, generated from the notes, configs, results and paper](https://raw.githubusercontent.com/simplifiedmachine/research-diff/main/docs/research.svg)
31
+
32
+ <details><summary>1 open warning</summary>
33
+
34
+ - H2 is open and no experiment tests it
35
+
36
+ </details>
37
+ <!-- research-map:end -->
38
+
39
+ The map above is this repository's own example (`examples/batch-size`), regenerated from its files on every change.
40
+
41
+ ## What it reads
42
+
43
+ Markers in your own files, in one grammar: an id (`Q` question, `H` hypothesis, `E` experiment, `R` result,
44
+ `C` claim, and a number) and a title, then links and a status on the lines after it.
45
+
46
+ ```markdown
47
+ ## H2: Learning-rate warm-up removes the gap <- notes (Markdown): a heading or a list item
48
+ answers: Q1
49
+ status: open <- open | supported | refuted | anything you like
50
+ ```
51
+
52
+ | Where | How |
53
+ |---|---|
54
+ | Notes (`.md`) | definitions as above; `tests:`, `of:`, `supports:`, `weakens:`, `relies on:`, `metrics: acc=0.91` |
55
+ | Experiment configs (YAML, JSON, TOML) | a `research:` block: `id`, `title`, `tests`, `status`, and `results: out/e1.json`, whose numbers become the result `E1.results` |
56
+ | Scripts and notebooks | a comment: `# research-diff: E2 tests H2` |
57
+ | The paper (LaTeX) | a comment: `% C1: Small batches win` and `% relies on: H1, R1`; the paragraph's `\cite{…}` become its sources; `% expects: E1.results acc.batch32 > 0.85` warns when a result stops meeting it |
58
+
59
+ The map is built from markers only, so the same files always give the same map. It warns when a claim relies on
60
+ nothing or on a refuted hypothesis, when a number no longer meets what a claim `expects:`, when an open hypothesis has no experiment, and when a link points at nothing.
61
+
62
+ ## Use
63
+
64
+ ```sh
65
+ pipx install research-diff
66
+ research-diff map --out docs # research.svg and research.json
67
+ research-diff diff --base HEAD # what your uncommitted iteration did
68
+ research-diff sync # MLflow / W&B runs into research/runs.json
69
+ research-diff checkpoint # mark what you have understood
70
+ research-diff diff # only what came after that
71
+ ```
72
+
73
+ A diff reads like: *Adds 1 hypothesis (H3); H1: open → refuted; E1.results acc.b32: 0.874 → 0.881 (+0.8%); 1 new
74
+ link; 2 new warnings (C1 relies on H1, which is refuted…).* It comes with the exact list (`plan.txt`) and a picture of
75
+ only what changed (`diff.svg`).
76
+
77
+ ## With AI coding agents
78
+
79
+ ```sh
80
+ pipx inject research-diff mcp
81
+ research-diff setup claude # a Stop hook, a skill and the MCP server (--scope project, --undo)
82
+ research-diff watch # http://127.0.0.1:8766/ : the research diff since your checkpoint, live
83
+ ```
84
+
85
+ After every Claude Code turn one line says what changed in the research. The agent's tools: `research_diff`,
86
+ `research_map`, `log_result` (records a run's metrics and what it supports or weakens in `research/log.md`, so it is
87
+ on the map and in git) and `checkpoint`. The pictures, snapshots and live page are
88
+ [system-diff](https://github.com/simplifiedmachine/system-diff)'s.
89
+
90
+ ## Experiment trackers
91
+
92
+ Runs logged to MLflow (its local `mlruns/` store) or W&B (its local `wandb/` folders) become results of the experiment
93
+ they are tagged with: `research-diff.experiment` (or `research_id`) as an MLflow tag or param, `research_id` in the W&B
94
+ config. Tracker folders are usually git-ignored, so `research-diff sync` copies their runs into `research/runs.json`,
95
+ which is tracked: the runs are then on the map, in git and in the next diff. The hook, the live page and the MCP tools
96
+ sync on their own.
97
+
98
+ ## Markers suggested by your LLM
99
+
100
+ ```sh
101
+ research-diff suggest # notes and paper files with no marker yet, or name the files
102
+ git apply .git/research-diff/suggest.patch
103
+ ```
104
+
105
+ Your LLM (the one your Claude Code uses) proposes markers for what the text already says. Each proposal is checked
106
+ before it reaches the patch: a real line of a file it was shown, a marker in that file's own form, and no id that is
107
+ already used. Nothing is written until you apply the patch.
108
+
109
+ ## License
110
+
111
+ MIT
@@ -0,0 +1,88 @@
1
+ # research-diff
2
+
3
+ Maps your research from its notes, experiment configs, results and paper (questions, hypotheses, experiments,
4
+ results, claims) and shows what each iteration changed. Built for exploring ideas with AI coding agents.
5
+
6
+ <!-- research-map:begin -->
7
+ ![Research map, generated from the notes, configs, results and paper](https://raw.githubusercontent.com/simplifiedmachine/research-diff/main/docs/research.svg)
8
+
9
+ <details><summary>1 open warning</summary>
10
+
11
+ - H2 is open and no experiment tests it
12
+
13
+ </details>
14
+ <!-- research-map:end -->
15
+
16
+ The map above is this repository's own example (`examples/batch-size`), regenerated from its files on every change.
17
+
18
+ ## What it reads
19
+
20
+ Markers in your own files, in one grammar: an id (`Q` question, `H` hypothesis, `E` experiment, `R` result,
21
+ `C` claim, and a number) and a title, then links and a status on the lines after it.
22
+
23
+ ```markdown
24
+ ## H2: Learning-rate warm-up removes the gap <- notes (Markdown): a heading or a list item
25
+ answers: Q1
26
+ status: open <- open | supported | refuted | anything you like
27
+ ```
28
+
29
+ | Where | How |
30
+ |---|---|
31
+ | Notes (`.md`) | definitions as above; `tests:`, `of:`, `supports:`, `weakens:`, `relies on:`, `metrics: acc=0.91` |
32
+ | Experiment configs (YAML, JSON, TOML) | a `research:` block: `id`, `title`, `tests`, `status`, and `results: out/e1.json`, whose numbers become the result `E1.results` |
33
+ | Scripts and notebooks | a comment: `# research-diff: E2 tests H2` |
34
+ | The paper (LaTeX) | a comment: `% C1: Small batches win` and `% relies on: H1, R1`; the paragraph's `\cite{…}` become its sources; `% expects: E1.results acc.batch32 > 0.85` warns when a result stops meeting it |
35
+
36
+ The map is built from markers only, so the same files always give the same map. It warns when a claim relies on
37
+ nothing or on a refuted hypothesis, when a number no longer meets what a claim `expects:`, when an open hypothesis has no experiment, and when a link points at nothing.
38
+
39
+ ## Use
40
+
41
+ ```sh
42
+ pipx install research-diff
43
+ research-diff map --out docs # research.svg and research.json
44
+ research-diff diff --base HEAD # what your uncommitted iteration did
45
+ research-diff sync # MLflow / W&B runs into research/runs.json
46
+ research-diff checkpoint # mark what you have understood
47
+ research-diff diff # only what came after that
48
+ ```
49
+
50
+ A diff reads like: *Adds 1 hypothesis (H3); H1: open → refuted; E1.results acc.b32: 0.874 → 0.881 (+0.8%); 1 new
51
+ link; 2 new warnings (C1 relies on H1, which is refuted…).* It comes with the exact list (`plan.txt`) and a picture of
52
+ only what changed (`diff.svg`).
53
+
54
+ ## With AI coding agents
55
+
56
+ ```sh
57
+ pipx inject research-diff mcp
58
+ research-diff setup claude # a Stop hook, a skill and the MCP server (--scope project, --undo)
59
+ research-diff watch # http://127.0.0.1:8766/ : the research diff since your checkpoint, live
60
+ ```
61
+
62
+ After every Claude Code turn one line says what changed in the research. The agent's tools: `research_diff`,
63
+ `research_map`, `log_result` (records a run's metrics and what it supports or weakens in `research/log.md`, so it is
64
+ on the map and in git) and `checkpoint`. The pictures, snapshots and live page are
65
+ [system-diff](https://github.com/simplifiedmachine/system-diff)'s.
66
+
67
+ ## Experiment trackers
68
+
69
+ Runs logged to MLflow (its local `mlruns/` store) or W&B (its local `wandb/` folders) become results of the experiment
70
+ they are tagged with: `research-diff.experiment` (or `research_id`) as an MLflow tag or param, `research_id` in the W&B
71
+ config. Tracker folders are usually git-ignored, so `research-diff sync` copies their runs into `research/runs.json`,
72
+ which is tracked: the runs are then on the map, in git and in the next diff. The hook, the live page and the MCP tools
73
+ sync on their own.
74
+
75
+ ## Markers suggested by your LLM
76
+
77
+ ```sh
78
+ research-diff suggest # notes and paper files with no marker yet, or name the files
79
+ git apply .git/research-diff/suggest.patch
80
+ ```
81
+
82
+ Your LLM (the one your Claude Code uses) proposes markers for what the text already says. Each proposal is checked
83
+ before it reaches the patch: a real line of a file it was shown, a marker in that file's own form, and no id that is
84
+ already used. Nothing is written until you apply the patch.
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1,78 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1284" height="296" viewBox="0 0 1284 296" role="img" aria-label="Research map">
2
+ <style>
3
+ text { font-family: 'Segoe UI', FreeSans, 'Helvetica Neue', Arial, sans-serif; }
4
+ .bg { fill: #f8f9fb; stroke: #e1e5eb; }
5
+ .col { font-size: 11px; font-weight: 600; letter-spacing: .05em; fill: #667385; }
6
+ .bn .box { fill: #ffffff; stroke: #e1e5eb; stroke-width: 1.2; }
7
+ .bn.t-store .box { fill: #e8f0fc; }
8
+ .bn.legacy .box { fill: #f8f9fb; stroke: #c3cad4; stroke-dasharray: 5 4; }
9
+ .bn.legacy .bn-t { fill: #3b4655; }
10
+ .bn .band { fill: #1f5fbf; }
11
+ .bn.undeclared .band { fill: #9a5600; }
12
+ .bn.s-added .box { stroke: #157347; stroke-width: 1.8; } .bn.s-added .band { fill: #157347; }
13
+ .bn.s-changed .box { stroke: #9a5600; stroke-width: 1.8; } .bn.s-changed .band { fill: #9a5600; }
14
+ .bn.s-removed .box { stroke: #c4321c; stroke-dasharray: 5 4; fill: #fdecea; } .bn.s-removed .band { fill: #c4321c; }
15
+ .bn-t { font-size: 13px; font-weight: 600; fill: #161c24; }
16
+ .bn-s { font-size: 11.5px; fill: #667385; }
17
+ .bn-p { font-size: 11.5px; fill: #3b4655; } .bn-p.code { font-family: Consolas, Inconsolata, 'DejaVu Sans Mono', monospace; } .bn-p.more { fill: #667385; }
18
+ .bn-p.s-added { fill: #157347; font-weight: 600; } .bn-p.s-changed { fill: #9a5600; font-weight: 600; }
19
+ .bn-p.s-removed { fill: #c4321c; text-decoration: line-through; }
20
+ .be { fill: none; stroke: #667385; stroke-width: 1.4; opacity: .75; }
21
+ .be.k-data { stroke: #1f5fbf; } .be.k-network { stroke: #9a5600; stroke-dasharray: 6 4; }
22
+ .be.s-added { stroke: #157347; stroke-width: 2.2; opacity: 1; }
23
+ .be.s-removed { stroke: #c4321c; stroke-dasharray: 5 4; opacity: 1; }
24
+ .ar { fill: #667385; } .ar.k-data { fill: #1f5fbf; } .ar.k-network { fill: #9a5600; }
25
+ .ar.s-added { fill: #157347; } .ar.s-removed { fill: #c4321c; } .ar.k-person { fill: #1f5fbf; }
26
+ .halo { paint-order: stroke; stroke: #f8f9fb; stroke-width: 4px; stroke-linejoin: round; }
27
+ .lg { font-size: 11.5px; fill: #667385; }
28
+ .lg.s-added { fill: #157347; font-weight: 600; } .lg.s-changed { fill: #9a5600; font-weight: 600; }
29
+ .lg.s-removed { fill: #c4321c; font-weight: 600; }
30
+ .foot { font-size: 11.5px; fill: #3b4655; }
31
+ .st .box { fill: #ffffff; stroke: #e1e5eb; stroke-width: 1.4; }
32
+ .st.hi .box { fill: #e8f0fc; stroke: #1f5fbf; }
33
+ .st-w { font-size: 13px; font-weight: 600; fill: #161c24; }
34
+ .fl { fill: none; stroke: #667385; stroke-width: 1.4; } .fl.loop { stroke-dasharray: 4 4; }
35
+ .fl.gate { stroke: #1f5fbf; stroke-dasharray: 2 4; opacity: .7; }
36
+ .fl-note { font-size: 11.5px; fill: #667385; }
37
+ .gate .box { fill: #e8f0fc; stroke: #1f5fbf; } .gate text { font-size: 12.5px; font-weight: 600; fill: #1f5fbf; }
38
+ .exit { font-size: 12.5px; font-weight: 600; fill: #161c24; }
39
+ .lane .box { fill: #ffffff; stroke: #e1e5eb; stroke-width: 1.2; }
40
+ .lane.person .box { fill: #e8f0fc; stroke: #1f5fbf; }
41
+ .life { stroke: #c9d0da; stroke-width: 1.2; stroke-dasharray: 3 4; }
42
+ .msg { fill: none; stroke: #667385; stroke-width: 1.4; }
43
+ .msg.frame { stroke: #1f5fbf; stroke-dasharray: 5 4; } .msg.person { stroke: #1f5fbf; stroke-width: 2; }
44
+ .msg-t { font-size: 11.5px; fill: #3b4655; } .msg-t.frame { fill: #1f5fbf; } .msg-t.person { fill: #1f5fbf; font-weight: 600; }
45
+ .num circle { fill: #667385; } .num.person circle { fill: #1f5fbf; }
46
+ .num text { fill: #ffffff; font-size: 10px; font-weight: 700; }
47
+ .note .box { fill: #fff3dd; stroke: #e9c98f; } .note text { font-size: 11.5px; fill: #9a5600; }
48
+ </style>
49
+ <defs><marker id="ar-call-same" class="ar k-call s-same" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z"/></marker><marker id="ar-data-same" class="ar k-data s-same" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z"/></marker></defs>
50
+ <rect class="bg" x="0.5" y="0.5" width="1283" height="295" rx="10"/>
51
+ <text class="col" x="14" y="18">QUESTIONS</text>
52
+ <text class="col" x="278" y="18">HYPOTHESES</text>
53
+ <text class="col" x="542" y="18">EXPERIMENTS</text>
54
+ <text class="col" x="806" y="18">RESULTS</text>
55
+ <text class="col" x="1070" y="18">CLAIMS</text>
56
+ <path class="be k-call s-same" d="M1070,52 C1038,52 1038,128 1006,128 L806,128 C774,128 774,102 742,102 L542,102 C513,102 513,52 484,52" marker-end="url(#ar-call-same)"><title>C1 → H1</title></path>
57
+ <path class="be k-call s-same" d="M1070,52 C1041,52 1041,157 1012,157" marker-end="url(#ar-call-same)"><title>C1 → R1</title></path>
58
+ <path class="be k-call s-same" d="M542,52 C513,52 513,52 484,52" marker-end="url(#ar-call-same)"><title>E1 → H1</title></path>
59
+ <path class="be k-call s-same" d="M806,52 C777,52 777,52 748,52" marker-end="url(#ar-call-same)"><title>E1.results → E1</title></path>
60
+ <path class="be k-call s-same" d="M278,52 C249,52 249,52 220,52" marker-end="url(#ar-call-same)"><title>H1 → Q1</title></path>
61
+ <path class="be k-call s-same" d="M278,128 C249,128 249,52 220,52" marker-end="url(#ar-call-same)"><title>H2 → Q1</title></path>
62
+ <path class="be k-call s-same" d="M806,157 C777,157 777,52 748,52" marker-end="url(#ar-call-same)"><title>R1 → E1</title></path>
63
+ <path class="be k-data s-same" d="M806,157 C774,157 774,104.5 742,104.5 L542,104.5 C513,104.5 513,52 484,52" marker-end="url(#ar-data-same)"><title>R1 → H1</title></path>
64
+ <g class="bn t-component s-same" transform="translate(14,30)"><rect class="box" width="200" height="62" rx="8"/><text x="12" y="19" class="bn-t">Q1 How does batch size</text><text x="12" y="35" class="bn-t">change accuracy when th…</text><text x="12" y="51" class="bn-s">examples/batch-size/notes/ideas…</text><title>Q1 How does batch size change accuracy when there are under 10k examples?</title></g>
65
+ <g class="bn t-component s-same new" transform="translate(278,30)"><rect class="box" width="200" height="62" rx="8"/><rect class="band" width="5" height="62" rx="2"/><text x="12" y="19" class="bn-t">H1 Smaller batches</text><text x="12" y="35" class="bn-t">generalise better on sm…</text><text x="12" y="51" class="bn-s">supported</text><title>H1 Smaller batches generalise better on small datasets</title></g>
66
+ <g class="bn t-component s-same" transform="translate(278,106)"><rect class="box" width="200" height="62" rx="8"/><text x="12" y="19" class="bn-t">H2 Learning-rate warm-up</text><text x="12" y="35" class="bn-t">removes the gap</text><text x="12" y="51" class="bn-s">open</text><title>H2 Learning-rate warm-up removes the gap</title></g>
67
+ <g class="bn t-component s-same" transform="translate(542,30)"><rect class="box" width="200" height="62" rx="8"/><text x="12" y="19" class="bn-t">E1 Batch 32 vs 512 on</text><text x="12" y="35" class="bn-t">small-8k</text><text x="12" y="51" class="bn-s">examples/batch-size/configs/e1.…</text><title>E1 Batch 32 vs 512 on small-8k</title></g>
68
+ <g class="bn t-store s-same" transform="translate(806,30)"><rect class="box" width="200" height="91" rx="8"/><text x="12" y="19" class="bn-t">E1.results</text><text x="12" y="35" class="bn-s">examples/batch-size/out/e1.json</text><text x="14" y="53" class="bn-p s-same code">· acc.batch32 = 0.874</text><text x="14" y="68" class="bn-p s-same code">· acc.batch512 = 0.842</text><text x="14" y="83" class="bn-p s-same code">· epochs = 30</text><title>E1.results</title></g>
69
+ <g class="bn t-store s-same" transform="translate(806,135)"><rect class="box" width="200" height="77" rx="8"/><text x="12" y="19" class="bn-t">R1 Batch 32 beats 512 by</text><text x="12" y="35" class="bn-t">3.2 points</text><text x="12" y="51" class="bn-s">examples/batch-size/notes/resul…</text><text x="14" y="69" class="bn-p s-same code">· gap = 0.032</text><title>R1 Batch 32 beats 512 by 3.2 points</title></g>
70
+ <g class="bn t-component s-same" transform="translate(1070,30)"><rect class="box" width="200" height="62" rx="8"/><text x="12" y="19" class="bn-t">C1 Small batches give</text><text x="12" y="35" class="bn-t">better accuracy on smal…</text><text x="12" y="51" class="bn-s">2 sources</text><title>C1 Small batches give better accuracy on small datasets</title></g>
71
+ <rect x="16" y="246" width="14" height="12" rx="2" fill="#ffffff" stroke="#e1e5eb"/><rect x="16" y="246" width="4" height="12" rx="1" fill="#1f5fbf"/>
72
+ <text x="36" y="256" class="lg">supported</text>
73
+ <path class="be k-call" d="M109.9,252 L129.9,252" style="opacity:1"/>
74
+ <text x="135.9" y="256" class="lg">links</text>
75
+ <path class="be k-data" d="M184.9,252 L204.9,252" style="opacity:1"/>
76
+ <text x="210.9" y="256" class="lg">supports</text>
77
+ <text class="foot" x="16" y="282">1 warning: H2 is open and no experiment tests it</text>
78
+ </svg>
@@ -0,0 +1,10 @@
1
+ # Batch 32 against batch 512 on the 8k-example split.
2
+ model: resnet18
3
+ dataset: small-8k
4
+ batch_sizes: [32, 512]
5
+
6
+ research:
7
+ id: E1
8
+ title: Batch 32 vs 512 on small-8k
9
+ tests: [H1]
10
+ results: ../out/e1.json
@@ -0,0 +1,15 @@
1
+ # Does batch size matter on small datasets?
2
+
3
+ ## Q1: How does batch size change accuracy when there are under 10k examples?
4
+
5
+ ## H1: Smaller batches generalise better on small datasets
6
+
7
+ answers: Q1
8
+ status: supported
9
+
10
+ ## H2: Learning-rate warm-up removes the gap
11
+
12
+ answers: Q1
13
+ status: open
14
+
15
+ The idea: the gap may be an optimisation artefact rather than a generalisation one.
@@ -0,0 +1,7 @@
1
+ # Results
2
+
3
+ ## R1: Batch 32 beats 512 by 3.2 points
4
+
5
+ of: E1
6
+ supports: H1
7
+ metrics: gap=0.032
@@ -0,0 +1 @@
1
+ {"acc": {"batch32": 0.874, "batch512": 0.842}, "epochs": 30}
@@ -0,0 +1,7 @@
1
+ \section{Results}
2
+
3
+ % C1: Small batches give better accuracy on small datasets
4
+ % relies on: H1, R1
5
+ % expects: E1.results acc.batch32 > 0.85
6
+ On the 8k split, batch 32 reaches 87.4\% against 84.2\% for batch 512, in line with
7
+ earlier work \cite{keskar2017large,masters2018revisiting}.
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.24"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "research-diff"
7
+ version = "0.1.0"
8
+ description = "Maps your research from its notes, experiment configs, results and paper (questions, hypotheses, experiments, results, claims) and shows what each iteration changed. Built for exploring ideas with AI coding agents."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ authors = [{ name = "simplifiedmachine" }]
14
+ keywords = ["research", "experiment-tracking", "hypotheses", "reproducibility", "ai-agents", "claude-code", "mcp"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Science/Research",
18
+ "Programming Language :: Python :: 3 :: Only",
19
+ "Topic :: Scientific/Engineering",
20
+ ]
21
+ # Its pictures, snapshots and live page are system-diff's (same author, no dependencies of its own), pinned.
22
+ dependencies = ["system-diff==0.1.0"]
23
+
24
+ [project.optional-dependencies]
25
+ mcp = ["mcp>=1.10"]
26
+ dev = ["pytest>=8", "mcp>=1.10"]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/simplifiedmachine/research-diff"
30
+ Documentation = "https://github.com/simplifiedmachine/research-diff/wiki"
31
+
32
+ [project.scripts]
33
+ research-diff = "research_diff.cli:main"
34
+ resdiff = "research_diff.cli:main"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/research_diff"]
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
@@ -0,0 +1,8 @@
1
+ # research-diff on its own example project: the README's map is drawn from examples/.
2
+ notes = ["examples/**/*.md"]
3
+ configs = ["examples/**/*.yaml", "examples/**/*.json"]
4
+ scripts = ["examples/**/*.py"]
5
+ paper = ["examples/**/*.tex"]
6
+
7
+ [readme]
8
+ image = "https://raw.githubusercontent.com/simplifiedmachine/research-diff/main/docs/research.svg" # shows on PyPI too
@@ -0,0 +1,2 @@
1
+ """research-diff: your research as a map, and what each iteration changed in it."""
2
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())