treegraphduals 0.1.0__tar.gz → 0.2.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.
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/.gitignore +5 -1
- treegraphduals-0.2.0/CHANGELOG.md +63 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/CITATION.cff +2 -2
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/PKG-INFO +32 -11
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/README.md +23 -2
- treegraphduals-0.2.0/docs/adr/0001-spec-0-support-policy.md +83 -0
- treegraphduals-0.2.0/docs/adr/0002-spec-0-windows-per-minor-version.md +46 -0
- treegraphduals-0.2.0/docs/adr/0003-no-free-threaded-ci-jobs.md +55 -0
- treegraphduals-0.2.0/docs/adr/0004-docs-build-runs-tests-once-and-reports-failures.md +60 -0
- treegraphduals-0.2.0/docs/adr/0005-security-fixes-for-latest-release-only.md +59 -0
- treegraphduals-0.2.0/docs/adr/0006-ai-provenance-tracking.md +126 -0
- treegraphduals-0.2.0/docs/adr/README.md +27 -0
- treegraphduals-0.2.0/docs/adr/template.md +24 -0
- treegraphduals-0.2.0/docs/ai_development.rst +189 -0
- treegraphduals-0.2.0/docs/ai_provenance.rst +57 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/conf.py +7 -1
- treegraphduals-0.2.0/docs/contributing.rst +175 -0
- treegraphduals-0.2.0/docs/definitions.md +93 -0
- treegraphduals-0.2.0/docs/index.rst +112 -0
- treegraphduals-0.2.0/docs/installation.rst +26 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/pyproject.toml +20 -9
- treegraphduals-0.2.0/tests/test_ai_provenance.py +711 -0
- treegraphduals-0.2.0/tests/test_guard_github.py +222 -0
- treegraphduals-0.2.0/uv.lock +1609 -0
- treegraphduals-0.1.0/CHANGELOG.md +0 -64
- treegraphduals-0.1.0/docs/contributing.rst +0 -4
- treegraphduals-0.1.0/docs/index.rst +0 -70
- treegraphduals-0.1.0/docs/installation.rst +0 -8
- treegraphduals-0.1.0/src/treegraphduals/agents/__init__.py +0 -4
- treegraphduals-0.1.0/uv.lock +0 -1383
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/LICENSE +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/Makefile +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/_static/.gitkeep +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/api/core.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/api/timeseries.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/api/visualizations.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/changelog.md +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/examples.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/quickstart.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/testing.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/docs/tutorial.rst +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/pytest.ini +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/__init__.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/__init__.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/base_graph.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/binary_tree.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/dag.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/erdos_renyi.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/forest.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/galton_watson.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/graph.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/multigraph.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/polytree.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/real_tree.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/core/tree.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/py.typed +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/timeseries/__init__.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/timeseries/timeseries.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/visualizations/__init__.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/visualizations/plot_combined.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/visualizations/plot_timeseries.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/src/treegraphduals/visualizations/plot_trees.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/tests/test_binary_tree.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/tests/test_timeseries.py +0 -0
- {treegraphduals-0.1.0 → treegraphduals-0.2.0}/tests/test_tree.py +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
4
|
+
|
|
5
|
+
## \[0.2.0\] - 2026-10-11
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Community health files: a Code of Conduct (Contributor Covenant v2.1), issue forms for bug reports, feature requests and math/definition questions, a pull request template, `SECURITY.md` and `SUPPORT.md`. Questions go to GitHub Discussions (#70).
|
|
10
|
+
- Contributing guide: the contribution process, requirements for code, and the test policy (#70, #83).
|
|
11
|
+
- README and docs badges, and weekly Dependabot updates for GitHub Actions and `uv.lock` (#60).
|
|
12
|
+
- A quarterly SPEC 0 workflow that opens a PR raising the Python and dependency floors, and `scripts/sync_python_versions.py`, which keeps every copy of the supported-Python list in sync with `pyproject.toml` (#78).
|
|
13
|
+
- AI-use disclosure: every agent-authored PR records the models, session, AI-generated files, human input and review rounds, and `CLAUDE.md` sets the provenance rules (#82).
|
|
14
|
+
- AI provenance tracking: each PR's human steering and AI/human line authorship is recorded, and the totals, plus a nightly mutation-testing score, are published on the "AI Provenance" docs page (#81, ADR 0006).
|
|
15
|
+
- Docs page: "How This Project Is Built with AI" (#72).
|
|
16
|
+
- Agent workflow tooling: the `/work-issue` skill, a `reviewer` subagent based on Matt Pocock's skills, and a hook that keeps pushes to `main`, PR merges and repository-settings changes with the maintainer, with a test suite for the hook (#71).
|
|
17
|
+
- Architecture decision records in `docs/adr/` (ADRs 0001–0006), and `.github/CODEOWNERS`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Breaking:** requires Python 3.13 or newer, and raises the minimum versions of NumPy (2.3), SciPy (1.15), Matplotlib (3.11), NetworkX (3.5), igraph (1.0) and SymPy (1.14), following the SPEC 0 support policy (ADRs 0001 and 0002). Python 3.12 users should stay on 0.1.0.
|
|
22
|
+
- CI tests Python 3.13 and 3.14 (required) and 3.15 (non-blocking), plus a non-blocking job on the minimum dependency versions. Free-threaded builds are not tested yet, because igraph has no free-threaded wheels (ADR 0003).
|
|
23
|
+
- Read the Docs builds run the test suite once. Failing tests no longer abort the docs build; the Testing page shows the failures instead (ADR 0004).
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- The empty `treegraphduals.agents` subpackage (planned LangGraph tooling, now out of scope) (#74).
|
|
28
|
+
|
|
29
|
+
### Security
|
|
30
|
+
|
|
31
|
+
- Every GitHub Action is pinned to a full commit SHA. Workflows that don't push no longer keep checkout credentials, and the release workflow defaults to a read-only token (#83).
|
|
32
|
+
- An OpenSSF Scorecard workflow and README badge, and zizmor checks of the workflow files in pre-commit (#83).
|
|
33
|
+
- GitHub releases carry Sigstore-signed build provenance; the docs explain how to verify it (#83).
|
|
34
|
+
- Dependabot version updates wait out a 7-day cooldown; security updates are not delayed (#83).
|
|
35
|
+
- Vulnerabilities are reported privately (see `SECURITY.md`), and fixes ship in the latest release only (ADR 0005).
|
|
36
|
+
|
|
37
|
+
## \[0.1.0\] - 2026-09-18
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- Initial project setup: `uv` packaging, Ruff lint/format, Pyrefly type checking, pytest + Hypothesis + coverage, Sphinx docs on Read the Docs, `CITATION.cff` with Zenodo DOI, pre-commit hooks.
|
|
42
|
+
- Packaging: Hatchling build backend, so the project can be built and published (`uv build`). Ships `py.typed`, the MIT license file, README as the long description, classifiers, keywords and project URLs.
|
|
43
|
+
- `tests/test_timeseries.py`: regression tests for the extrema cases below, the numerical fallback path, uniform sampling, and level-set tree construction.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **Breaking:** moved `core`, `timeseries`, `visualizations` and `agents` under a single `src/treegraphduals/` package. Imports are now `from treegraphduals.core import Tree` rather than `from core import Tree`, so the library no longer claims generic top-level names.
|
|
48
|
+
- `requires-python` raised to `>=3.12`, matching what CI and Read the Docs test.
|
|
49
|
+
- Test and documentation tooling moved out of runtime dependencies into the `dev` and `docs` dependency groups; a plain `pip install treegraphduals` no longer pulls in pytest and Sphinx.
|
|
50
|
+
- Doctests now run by default: `testpaths` covers `src` as well as `tests`, so the `--doctest-modules` flag in CI is no longer a no-op (62 tests, up from 40).
|
|
51
|
+
- `docs/conf.py` reads the version from package metadata instead of hardcoding it, and no longer injects an absolute local path for doctest setup.
|
|
52
|
+
- Replaced a `print` with `warnings.warn` when symbolic differentiation falls back to the numerical extrema search.
|
|
53
|
+
- Cleared the Ruff backlog (56 findings): added missing module and magic-method docstrings, made ASCII-art docstrings raw so the diagrams render literally, replaced implicit `Optional` annotations, narrowed a bare `except`, collapsed nested conditionals, sorted `__all__` lists, and dropped dead locals.
|
|
54
|
+
- README reorganized into what the package computes today versus what is planned. Every original bullet is preserved; the previous single list mixed 25+ unimplemented features (duals, visibility graphs, merge trees, graph metrics) with the 7 that work, and it is the PyPI long description.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- Docstring tree diagrams rendered mangled: 15 docstrings were not raw strings, so a trailing `\` in the ASCII art silently swallowed the following newline and joined rows together (`/ \` + `1 2` became `/ 1 2`). The art in the source was correct all along. Diagrams are now raw strings inside RST literal blocks, and the Horton pruning and Horton-Strahler examples are redrawn from what the algorithms actually produce.
|
|
59
|
+
- The Sphinx build is now warning-free (was 84 warnings).
|
|
60
|
+
- Symbolic extrema search (`TimeSeries.from_function(..., preserve_extrema=True)`) no longer silently discards critical points:
|
|
61
|
+
- Real roots that sympy returns in complex form (Cardano's formula writes the three real roots of a cubic using `I`) were dropped, because the reality test was "does `float()` succeed". Reality is now decided by the magnitude of the imaginary part, so e.g. `t**4/4 - 2*t**2 + t/3` reports its three extrema instead of none.
|
|
62
|
+
- Periodic derivatives only ever produced sympy's principal solutions, so `sin(t)` on `[0, 10]` lost its maximum at `5*pi/2`. Critical points are now solved over the interval, which enumerates the periodic solutions.
|
|
63
|
+
- `plot_tree_and_harris_path` no longer leaks one matplotlib figure per call (pyplot kept every discarded `plot_tree` figure alive), and now draws the tree into its left axes instead of a "Tree plot" placeholder.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
cff-version: 1.2.0
|
|
2
2
|
message: "If you use this software in your work, please cite it as below."
|
|
3
3
|
title: "treegraphduals"
|
|
4
|
-
version: "0.
|
|
5
|
-
date-released: "2026-
|
|
4
|
+
version: "0.2.0"
|
|
5
|
+
date-released: "2026-10-12"
|
|
6
6
|
authors:
|
|
7
7
|
- family-names: "Khan"
|
|
8
8
|
given-names: "Ibraheem"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: treegraphduals
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Trees, their duals, graphs, and time series: level-set trees, Harris paths, Horton-Strahler pruning, and horizontal tunnelability graphs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/pikhan/treegraphduals
|
|
6
6
|
Project-URL: Repository, https://github.com/pikhan/treegraphduals
|
|
@@ -15,21 +15,33 @@ Keywords: duality,graphs,harris paths,horton-strahler,level-set trees,random sel
|
|
|
15
15
|
Classifier: Development Status :: 3 - Alpha
|
|
16
16
|
Classifier: Intended Audience :: Science/Research
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
|
-
Requires-Python: >=3.
|
|
23
|
-
Requires-Dist: igraph>=0
|
|
24
|
-
Requires-Dist: matplotlib>=3.
|
|
25
|
-
Requires-Dist: networkx>=3.
|
|
26
|
-
Requires-Dist: numpy>=
|
|
27
|
-
Requires-Dist: scipy>=1.
|
|
28
|
-
Requires-Dist: sympy>=1.
|
|
22
|
+
Requires-Python: >=3.13
|
|
23
|
+
Requires-Dist: igraph>=1.0
|
|
24
|
+
Requires-Dist: matplotlib>=3.11
|
|
25
|
+
Requires-Dist: networkx>=3.5
|
|
26
|
+
Requires-Dist: numpy>=2.3
|
|
27
|
+
Requires-Dist: scipy>=1.15
|
|
28
|
+
Requires-Dist: sympy>=1.14
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
|
-
[](https://github.com/pikhan/treegraphduals/actions/workflows/ci.yml)
|
|
32
|
+
[](https://github.com/pikhan/treegraphduals/actions/workflows/ci.yml)
|
|
33
|
+
[](https://github.com/astral-sh/ruff)
|
|
34
|
+
[](https://pypi.org/project/treegraphduals/)
|
|
35
|
+
[](https://pypi.org/project/treegraphduals/)
|
|
36
|
+
[](https://github.com/pikhan/treegraphduals/blob/main/LICENSE)
|
|
32
37
|
[](https://treegraphduals.readthedocs.io/en/latest/)
|
|
38
|
+
[](https://doi.org/10.5281/zenodo.22839827)
|
|
39
|
+
[](https://scorecard.dev/viewer/?uri=github.com/pikhan/treegraphduals)
|
|
40
|
+
[](https://www.bestpractices.dev/projects/15397)
|
|
41
|
+
|
|
42
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
43
|
+
[](https://pyrefly.org/)
|
|
44
|
+
[](https://hypothesis.readthedocs.io/)
|
|
33
45
|
|
|
34
46
|
# treegraphduals
|
|
35
47
|
|
|
@@ -46,7 +58,7 @@ the piece you need is ready.
|
|
|
46
58
|
pip install treegraphduals # or: uv add treegraphduals
|
|
47
59
|
```
|
|
48
60
|
|
|
49
|
-
Requires Python 3.
|
|
61
|
+
Requires Python 3.13+.
|
|
50
62
|
|
|
51
63
|
## Quickstart
|
|
52
64
|
|
|
@@ -167,6 +179,15 @@ The graph classes are intended to become general enough to allow for:
|
|
|
167
179
|
|
|
168
180
|
Full API documentation, tutorials, and examples: <https://treegraphduals.readthedocs.io>
|
|
169
181
|
|
|
182
|
+
## Getting help and contributing
|
|
183
|
+
|
|
184
|
+
- Questions: [GitHub Discussions](https://github.com/pikhan/treegraphduals/discussions) (Q&A; definition
|
|
185
|
+
questions go in Math and Definitions).
|
|
186
|
+
- Bugs, feature requests and definition disagreements: [issues](https://github.com/pikhan/treegraphduals/issues/new/choose).
|
|
187
|
+
- Security vulnerabilities: report privately, see [`SECURITY.md`](SECURITY.md).
|
|
188
|
+
- Contributing: see the [contributing guide](https://treegraphduals.readthedocs.io/en/latest/contributing.html).
|
|
189
|
+
Everyone taking part follows the [Code of Conduct](CODE_OF_CONDUCT.md); [`SUPPORT.md`](SUPPORT.md) has the details.
|
|
190
|
+
|
|
170
191
|
## Citing
|
|
171
192
|
|
|
172
193
|
If you use this package, please cite it via the DOI above, or see
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
[](https://github.com/pikhan/treegraphduals/actions/workflows/ci.yml)
|
|
2
|
+
[](https://github.com/pikhan/treegraphduals/actions/workflows/ci.yml)
|
|
3
|
+
[](https://github.com/astral-sh/ruff)
|
|
4
|
+
[](https://pypi.org/project/treegraphduals/)
|
|
5
|
+
[](https://pypi.org/project/treegraphduals/)
|
|
6
|
+
[](https://github.com/pikhan/treegraphduals/blob/main/LICENSE)
|
|
2
7
|
[](https://treegraphduals.readthedocs.io/en/latest/)
|
|
8
|
+
[](https://doi.org/10.5281/zenodo.22839827)
|
|
9
|
+
[](https://scorecard.dev/viewer/?uri=github.com/pikhan/treegraphduals)
|
|
10
|
+
[](https://www.bestpractices.dev/projects/15397)
|
|
11
|
+
|
|
12
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
13
|
+
[](https://pyrefly.org/)
|
|
14
|
+
[](https://hypothesis.readthedocs.io/)
|
|
3
15
|
|
|
4
16
|
# treegraphduals
|
|
5
17
|
|
|
@@ -16,7 +28,7 @@ the piece you need is ready.
|
|
|
16
28
|
pip install treegraphduals # or: uv add treegraphduals
|
|
17
29
|
```
|
|
18
30
|
|
|
19
|
-
Requires Python 3.
|
|
31
|
+
Requires Python 3.13+.
|
|
20
32
|
|
|
21
33
|
## Quickstart
|
|
22
34
|
|
|
@@ -137,6 +149,15 @@ The graph classes are intended to become general enough to allow for:
|
|
|
137
149
|
|
|
138
150
|
Full API documentation, tutorials, and examples: <https://treegraphduals.readthedocs.io>
|
|
139
151
|
|
|
152
|
+
## Getting help and contributing
|
|
153
|
+
|
|
154
|
+
- Questions: [GitHub Discussions](https://github.com/pikhan/treegraphduals/discussions) (Q&A; definition
|
|
155
|
+
questions go in Math and Definitions).
|
|
156
|
+
- Bugs, feature requests and definition disagreements: [issues](https://github.com/pikhan/treegraphduals/issues/new/choose).
|
|
157
|
+
- Security vulnerabilities: report privately, see [`SECURITY.md`](SECURITY.md).
|
|
158
|
+
- Contributing: see the [contributing guide](https://treegraphduals.readthedocs.io/en/latest/contributing.html).
|
|
159
|
+
Everyone taking part follows the [Code of Conduct](CODE_OF_CONDUCT.md); [`SUPPORT.md`](SUPPORT.md) has the details.
|
|
160
|
+
|
|
140
161
|
## Citing
|
|
141
162
|
|
|
142
163
|
If you use this package, please cite it via the DOI above, or see
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# 0001. Follow SPEC 0 for supported Python and dependency versions
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-10-10
|
|
5
|
+
- **Issues:** #76, #77, #78, #75, #79
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
treegraphduals is a scientific Python library. Its core dependencies (NumPy,
|
|
10
|
+
SciPy, Matplotlib, NetworkX) follow [SPEC 0](https://scientific-python.org/specs/spec-0000/)
|
|
11
|
+
and drop old versions on a published quarterly schedule. SPEC 0 recommends
|
|
12
|
+
dropping a Python version **3 years** after its release and a core package
|
|
13
|
+
version **2 years** after its release. The stated purpose is to limit
|
|
14
|
+
maintenance burden. Projects may drop earlier when a critical fix isn't
|
|
15
|
+
backported. SPEC 0 doesn't forbid supporting versions longer.
|
|
16
|
+
|
|
17
|
+
Before this ADR, the floors (`requires-python >=3.12`, `numpy>=1.26`,
|
|
18
|
+
`scipy>=1.11`, `matplotlib>=3.8`, `networkx>=3.2`) were all older than SPEC 0
|
|
19
|
+
allows. CI tested none of them: it tested only the latest locked versions, on
|
|
20
|
+
one interpreter. The floors were claims, not tested facts.
|
|
21
|
+
|
|
22
|
+
Installers honor `Requires-Python`. When a release drops a Python version,
|
|
23
|
+
users on that version keep getting the last release that supported it.
|
|
24
|
+
Dropping a version never breaks existing users.
|
|
25
|
+
|
|
26
|
+
JOSS and pyOpenSci reviewers (#64) look for a stated support policy.
|
|
27
|
+
|
|
28
|
+
## Options considered
|
|
29
|
+
|
|
30
|
+
- **A. Follow SPEC 0.** Drop each Python version 3 years after release and each
|
|
31
|
+
core-package version 2 years after release, quarterly. This is the same
|
|
32
|
+
schedule our dependencies use, and it keeps CI small.
|
|
33
|
+
- **B. SPEC 0, but keep each Python version once it works ("perpetual
|
|
34
|
+
support").** The claim decays. Once NumPy and SciPy stop publishing wheels
|
|
35
|
+
for a version, CI on it tests ever-older dependencies that nobody else tests.
|
|
36
|
+
CPython itself stops security fixes 5 years after release (3.13: October 2029).
|
|
37
|
+
"It worked when we tested it" says nothing about code written later. Users
|
|
38
|
+
on old versions are already served by old releases (see Context).
|
|
39
|
+
- **C. A fixed floor, bumped when someone remembers.** What the project did
|
|
40
|
+
until now. The floors drifted (see Context).
|
|
41
|
+
- **D. NEP 29.** Superseded by SPEC 0.
|
|
42
|
+
|
|
43
|
+
## Decision
|
|
44
|
+
|
|
45
|
+
1. **Python.** Support each CPython minor version until its SPEC 0 drop date
|
|
46
|
+
(3 years after its x.y.0 release). The `requires-python` floor is the oldest
|
|
47
|
+
version still in that window. Drop earlier only if a dependency we need
|
|
48
|
+
forces it. Python 3.12 is dropped at the next release (#77), at its SPEC 0
|
|
49
|
+
date (2026 Q4), or a few days before it if that release lands before
|
|
50
|
+
2026-10-01.
|
|
51
|
+
2. **Core dependencies** (the SPEC 0 core packages we depend on: NumPy, SciPy,
|
|
52
|
+
Matplotlib, NetworkX). Floors follow SPEC 0's 2-year window.
|
|
53
|
+
3. **Other runtime dependencies** (igraph, SymPy, and any added later). Floors
|
|
54
|
+
follow the same 2-year window (spec0-action's `update_all: 2`). They are
|
|
55
|
+
raised further when CI's lowest-direct job (#78) shows they must be.
|
|
56
|
+
4. **Cadence.** Floors are bumped quarterly by an automated PR (#78) that a
|
|
57
|
+
maintainer reviews and merges. The PR's GitHub notification (email) is the
|
|
58
|
+
maintainer's prompt to act; no separate calendar or email reminder is
|
|
59
|
+
required. A release after a quarter boundary includes that quarter's drops.
|
|
60
|
+
5. **New Python versions.** As soon as uv can install a prerelease, add it and
|
|
61
|
+
its free-threaded build to CI as non-blocking jobs. Make the normal build
|
|
62
|
+
blocking, and add its classifier, once the final release is out, every
|
|
63
|
+
runtime dependency installs from wheels, and the job has been green on
|
|
64
|
+
`main` (#79 for 3.15).
|
|
65
|
+
6. **Free-threaded builds** are tested in non-blocking jobs only. We don't
|
|
66
|
+
claim free-threading support (no classifier, no thread-safety guarantee)
|
|
67
|
+
until a separate ADR decides to.
|
|
68
|
+
7. **Development** happens on the newest blocking version. CI's lint and
|
|
69
|
+
type-check job runs on the floor.
|
|
70
|
+
8. **No upper bounds** on dependencies. `uv.lock` pins exact versions for
|
|
71
|
+
development.
|
|
72
|
+
|
|
73
|
+
## Consequences
|
|
74
|
+
|
|
75
|
+
- The next release requires Python 3.13+ and raises the dependency floors
|
|
76
|
+
(#77). Python 3.12 users keep getting 0.1.x.
|
|
77
|
+
- Expected Python drops: 3.13 in 2027 Q4, 3.14 in 2028 Q4.
|
|
78
|
+
- Every quarter an automated PR arrives. CI must pass on the new floors,
|
|
79
|
+
including the lowest-direct job.
|
|
80
|
+
- The supported-version list lives in several files (pyproject, CI matrix,
|
|
81
|
+
`.readthedocs.yaml`, the release smoke test, README, CLAUDE.md). #78
|
|
82
|
+
keeps them in sync.
|
|
83
|
+
- Rules out perpetual support (option B) and unscheduled floor bumps (option C).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 0002. Count SPEC 0 dependency windows per minor version
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Date:** 2026-10-11
|
|
5
|
+
- **Issues:** #77, #78
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
[ADR 0001](0001-spec-0-support-policy.md) decisions 2 and 3 set dependency
|
|
10
|
+
floors by a 2-year window, but don't say what the window is counted over. Two
|
|
11
|
+
readings give different floors:
|
|
12
|
+
|
|
13
|
+
- **Per release:** the floor is the oldest *release* (any patch) from the last
|
|
14
|
+
2 years. In 2026 Q4 this gives `igraph>=0.11.8` (released 2024-10-28).
|
|
15
|
+
- **Per minor version:** the floor is the oldest *minor version* whose x.y.0
|
|
16
|
+
release came out in the last 2 years. igraph 0.11's first release was
|
|
17
|
+
2023-10-12, so 0.11 is out and the floor is `igraph>=1.0` (2025-10-23).
|
|
18
|
+
|
|
19
|
+
[SPEC 0](https://scientific-python.org/specs/spec-0000/) uses the
|
|
20
|
+
per-minor-version reading: its drop schedule lists minor versions by the date of
|
|
21
|
+
their x.y.0 release. spec0-action, which #78 uses to automate the quarterly
|
|
22
|
+
bumps, does the same.
|
|
23
|
+
|
|
24
|
+
## Options considered
|
|
25
|
+
|
|
26
|
+
- **Per release.** Floors can land on a patch release. This is not what SPEC 0
|
|
27
|
+
or spec0-action computes, so the automated bumps (#78) would disagree with
|
|
28
|
+
the floors set by hand.
|
|
29
|
+
- **Per minor version.** Floors move in bigger steps (igraph skips from
|
|
30
|
+
0.11.x straight to 1.0), but they match SPEC 0 and the tool that maintains
|
|
31
|
+
them.
|
|
32
|
+
|
|
33
|
+
## Decision
|
|
34
|
+
|
|
35
|
+
Every dependency floor under ADR 0001 decisions 2 and 3, for core and other
|
|
36
|
+
runtime dependencies alike, is the oldest minor version whose x.y.0 release is
|
|
37
|
+
within the 2-year window, written `>=x.y`. Floors are never set to a patch
|
|
38
|
+
release. Python versions are already counted per minor version (ADR 0001
|
|
39
|
+
decision 1).
|
|
40
|
+
|
|
41
|
+
## Consequences
|
|
42
|
+
|
|
43
|
+
- #77 sets `igraph>=1.0`, not `>=0.11.8`. SymPy is `>=1.14` under either
|
|
44
|
+
reading.
|
|
45
|
+
- #78's spec0-action configuration must use this rule. Its default already does.
|
|
46
|
+
- ADR 0001 is otherwise unchanged.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 0003. Don't test free-threaded Python builds in CI for now
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Date:** 2026-10-11
|
|
5
|
+
- **Issues:** #75, #79
|
|
6
|
+
- **Supersedes:** decision 6, and the free-threaded part of decision 5, of
|
|
7
|
+
[ADR 0001](0001-spec-0-support-policy.md)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
[ADR 0001](0001-spec-0-support-policy.md) decision 6 put free-threaded builds
|
|
12
|
+
(3.14t, 3.15t) in non-blocking CI jobs as an early warning. #75 added those
|
|
13
|
+
jobs. On their first runs (PR #88), neither job got as far as running a test:
|
|
14
|
+
|
|
15
|
+
- igraph 1.0.0, a runtime dependency, publishes only limited-API (`abi3`)
|
|
16
|
+
wheels. Free-threaded CPython rejects the limited API
|
|
17
|
+
(`#error "The limited API is not currently supported in the free-threaded
|
|
18
|
+
build"`), so there is no wheel to install and the source build fails.
|
|
19
|
+
- NumPy, SciPy, Matplotlib and Pillow already ship free-threaded wheels. igraph
|
|
20
|
+
is the only blocker.
|
|
21
|
+
|
|
22
|
+
We can't fix this. Until igraph ships free-threaded wheels, these jobs fail at
|
|
23
|
+
`uv sync` on every PR. They test none of our code, and a permanent red ✗ teaches
|
|
24
|
+
everyone to ignore red marks.
|
|
25
|
+
|
|
26
|
+
We don't claim free-threading support, and the ensembles in #49 parallelize just
|
|
27
|
+
as well with a process pool on any Python. Nothing we ship depends on free
|
|
28
|
+
threading.
|
|
29
|
+
|
|
30
|
+
## Options considered
|
|
31
|
+
|
|
32
|
+
- **A. Keep the jobs, non-blocking (ADR 0001 decision 6).** They give no signal
|
|
33
|
+
until igraph changes, and every PR shows red marks until then. Each new
|
|
34
|
+
free-threaded version would then repeat the wait for dependency wheels.
|
|
35
|
+
- **B. Remove the jobs, and revisit once igraph ships free-threaded wheels.**
|
|
36
|
+
No noise, and no loss of signal, because the jobs test nothing today.
|
|
37
|
+
- **C. Build igraph from source in CI without the limited API.** That would
|
|
38
|
+
mean maintaining a build of a C extension we don't own, for a feature we
|
|
39
|
+
don't claim.
|
|
40
|
+
|
|
41
|
+
## Decision
|
|
42
|
+
|
|
43
|
+
1. CI runs no free-threaded (`t`) Python builds. This supersedes ADR 0001
|
|
44
|
+
decision 6.
|
|
45
|
+
2. We still claim no free-threading support: no classifier, and no
|
|
46
|
+
thread-safety guarantee.
|
|
47
|
+
3. Revisit once igraph publishes free-threaded wheels (`cp3XXt`) on PyPI. Then a
|
|
48
|
+
new ADR can restore non-blocking free-threaded jobs.
|
|
49
|
+
|
|
50
|
+
## Consequences
|
|
51
|
+
|
|
52
|
+
- `ci.yml` drops the 3.14t and 3.15t jobs (#75).
|
|
53
|
+
- #79 covers only the normal 3.15 build. 3.15t is no longer in CI.
|
|
54
|
+
- ADR 0001 decision 5's "and its free-threaded build" no longer applies to new
|
|
55
|
+
Python versions.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 0004. Read the Docs runs the test suite once and publishes failures
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Date:** 2026-10-11
|
|
5
|
+
- **Issues:** #31
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The Testing page (`docs/testing.rst`) includes `docs/test_results.txt` and
|
|
10
|
+
`docs/coverage_report.md`, and links to the HTML coverage report. These files
|
|
11
|
+
are gitignored. Read the Docs (RTD) generates them in its `pre_build` step.
|
|
12
|
+
|
|
13
|
+
That step ran the suite twice. The first run aborted the build on any failure,
|
|
14
|
+
so the `|| true` on the second run, which wrote `test_results.txt`, never took
|
|
15
|
+
effect. The page could only ever show green, and each docs build took twice
|
|
16
|
+
as long as it needed to. `pytest.ini`'s `addopts` already add coverage, the HTML
|
|
17
|
+
report and doctests to every run, so one run produces everything the page needs.
|
|
18
|
+
|
|
19
|
+
CI already runs the full suite, and its `test` check is required to merge into
|
|
20
|
+
`main`.
|
|
21
|
+
|
|
22
|
+
## Options considered
|
|
23
|
+
|
|
24
|
+
- **A. Move report generation into CI, and have RTD only build Sphinx.** RTD
|
|
25
|
+
can't download GitHub Actions artifacts without a token, and those artifacts
|
|
26
|
+
expire. RTD builds start on push, often before CI finishes, so the reports
|
|
27
|
+
would lag a commit behind. Too much machinery for the real problem, which is
|
|
28
|
+
the doubled run time.
|
|
29
|
+
- **B. Gate: run once, and a failing test aborts the docs build.** A build that
|
|
30
|
+
aborts leaves RTD serving the last successful build. If broken code reached
|
|
31
|
+
`main`, the docs would keep showing stale green results.
|
|
32
|
+
- **C. Report: run once, and a failing test doesn't abort the build.** The docs
|
|
33
|
+
always match `main`. Normally `main` is green, because CI blocks broken
|
|
34
|
+
merges, so the page is green. If broken code gets in anyway, the page shows it.
|
|
35
|
+
|
|
36
|
+
## Decision
|
|
37
|
+
|
|
38
|
+
Option C, with tests that fail to load still aborting the build (maintainer,
|
|
39
|
+
on #31 and in the PR #98 session):
|
|
40
|
+
|
|
41
|
+
1. RTD keeps generating the test and coverage reports. They don't move to CI.
|
|
42
|
+
2. `pre_build` runs pytest once. Its output goes to `docs/test_results.txt`,
|
|
43
|
+
and the coverage reports (`coverage_report.md`, `coverage.json`, `htmlcov/`)
|
|
44
|
+
come from that same run's data.
|
|
45
|
+
3. Failing tests (pytest exit code 1) don't abort the docs build. The failures
|
|
46
|
+
appear on the Testing page.
|
|
47
|
+
4. If the suite doesn't run properly (exit code 2 or higher: tests that fail to
|
|
48
|
+
load, no tests collected, or a pytest error), the docs build aborts. The
|
|
49
|
+
same failure already fails CI and blocks the merge. In that case RTD keeps
|
|
50
|
+
serving the last good build, rather than publishing a page with no results
|
|
51
|
+
and no coverage.
|
|
52
|
+
5. CI's required `test` check, not the docs build, is the merge gate.
|
|
53
|
+
|
|
54
|
+
## Consequences
|
|
55
|
+
|
|
56
|
+
- `.readthedocs.yaml` writes the single pytest run to `test_results.txt`, echoes
|
|
57
|
+
it to the build log, and aborts only when pytest's exit code is 2 or higher
|
|
58
|
+
(#31).
|
|
59
|
+
- A red Testing page on `main` means broken code got past CI: treat it as a bug.
|
|
60
|
+
- A green docs build no longer means the tests passed. Check CI for that.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 0005. Security fixes go into the latest release only
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Date:** 2026-10-11
|
|
5
|
+
- **Issues:** #70, #76, #83
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`SECURITY.md` (#70) has to say which versions get security fixes. GitHub shows
|
|
10
|
+
the policy under the repository's Security tab, the OpenSSF Scorecard
|
|
11
|
+
Security-Policy check looks for it (#83), and JOSS and pyOpenSci reviewers
|
|
12
|
+
expect one (#64).
|
|
13
|
+
|
|
14
|
+
Vulnerabilities are reported privately through GitHub's private vulnerability
|
|
15
|
+
reporting (#83). A fix is coordinated in a GitHub security advisory and
|
|
16
|
+
published with a release.
|
|
17
|
+
|
|
18
|
+
The project has one maintainer, and releases are cut from `main`. There are no
|
|
19
|
+
maintenance branches.
|
|
20
|
+
|
|
21
|
+
[ADR 0001](0001-spec-0-support-policy.md) has the project follow SPEC 0. Each
|
|
22
|
+
release drops the Python and dependency versions that have left the SPEC 0
|
|
23
|
+
window. Installers honor `Requires-Python`, so users on a dropped Python keep
|
|
24
|
+
getting the last release that supported it. ADR 0001 covers which versions a
|
|
25
|
+
*release* supports. It doesn't say which *releases* get fixes.
|
|
26
|
+
|
|
27
|
+
## Options considered
|
|
28
|
+
|
|
29
|
+
- **A. Latest release only.** Fixes ship in a new release from `main`, and
|
|
30
|
+
nothing is backported. This is the least work, and it's consistent with
|
|
31
|
+
ADR 0001: old releases are for old environments, not maintained lines. Users on a Python
|
|
32
|
+
that SPEC 0 has dropped don't get the fix.
|
|
33
|
+
- **B. The latest release, plus backports to the previous minor series.** This
|
|
34
|
+
needs maintenance branches, a release process for each one, and CI for
|
|
35
|
+
dependency floors that `main` no longer tests. One maintainer can't keep
|
|
36
|
+
that up.
|
|
37
|
+
- **C. The last release for every Python version still supported by CPython.**
|
|
38
|
+
This is the most generous option. It brings back the old-floor maintenance
|
|
39
|
+
that ADR 0001 ruled out, and the dependencies themselves stop releasing for
|
|
40
|
+
those versions.
|
|
41
|
+
|
|
42
|
+
## Decision
|
|
43
|
+
|
|
44
|
+
1. Security fixes are made **only in a new release** cut from `main`. Earlier
|
|
45
|
+
releases get no fixes, and nothing is backported.
|
|
46
|
+
2. That release follows ADR 0001, so it supports only the Python and
|
|
47
|
+
dependency versions in the SPEC 0 window when it is made.
|
|
48
|
+
3. The advisory published with the fix names the affected versions and the
|
|
49
|
+
fixed version, so users on older releases can see that they are affected.
|
|
50
|
+
4. `SECURITY.md` states this policy, and it cites this ADR.
|
|
51
|
+
|
|
52
|
+
## Consequences
|
|
53
|
+
|
|
54
|
+
- To get a fix, users upgrade to the latest release. A user on a Python that
|
|
55
|
+
SPEC 0 has dropped has to upgrade Python first.
|
|
56
|
+
- No maintenance branches or backport process are needed.
|
|
57
|
+
- Changing this policy, for example to backport to the previous minor series
|
|
58
|
+
once the project has more maintainers, takes a new ADR that supersedes this
|
|
59
|
+
one.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# 0006. AI provenance: what is tracked, how, and where it is published
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Date:** 2026-10-11
|
|
5
|
+
- **Issues:** #81, #80, #72
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
JOSS and pyOpenSci both require a disclosure of AI use (#80), and the
|
|
10
|
+
maintainer wants to see where human effort goes: into prompting and into code.
|
|
11
|
+
Most of the evidence can't be reconstructed later. Commits carry no AI trailers,
|
|
12
|
+
squash merges collapse branch history, and Claude Code deletes session
|
|
13
|
+
transcripts after `cleanupPeriodDays` (30 days by default).
|
|
14
|
+
|
|
15
|
+
#81 left five decisions to the maintainer. They were settled in the issue
|
|
16
|
+
(the maintainer's comment of 2026-10-11, US time) and in the `/work-issue 81` session:
|
|
17
|
+
|
|
18
|
+
- **(a) Tool.** git-ai would work, but the goal is to see where human effort
|
|
19
|
+
went, not attribution alone. Custom scripts only if git-ai can't do it.
|
|
20
|
+
- **(b) Formal approvals.** No bot identity. Commits and PRs stay under the
|
|
21
|
+
maintainer's identity, and the maintainer reviews and merges every PR.
|
|
22
|
+
- **(c) Merge strategy.** Keep squash merges. The PR process doesn't change.
|
|
23
|
+
- **(d) What to publish.** Aggregates and per-PR numbers. No transcripts.
|
|
24
|
+
- **(e) Retention.** The 30-day default is fine.
|
|
25
|
+
|
|
26
|
+
## Options considered
|
|
27
|
+
|
|
28
|
+
- **git-ai** (open source, Apache-2.0) records line-level AI authorship in git
|
|
29
|
+
notes through agent hooks. It needs a background daemon and user-level hooks
|
|
30
|
+
installed by `curl | bash`. Keeping notes through GitHub's squash merge needs a
|
|
31
|
+
CI workflow that installs git-ai the same way and pushes with a
|
|
32
|
+
`contents: write` token. That cuts against the workflow hardening of #97, and
|
|
33
|
+
git-ai doesn't measure prompting effort at all.
|
|
34
|
+
- **A per-PR count from transcripts and commits.** One stdlib-only script reads
|
|
35
|
+
the transcripts for steering, decides per commit whether an agent made it,
|
|
36
|
+
and blames the PR's added lines. It needs no new tool and no write-token
|
|
37
|
+
workflow beyond the existing pattern, but gives per-PR totals, not per-line
|
|
38
|
+
blame on `main`.
|
|
39
|
+
|
|
40
|
+
The maintainer chose the per-PR count.
|
|
41
|
+
|
|
42
|
+
## Decision
|
|
43
|
+
|
|
44
|
+
`scripts/ai_provenance.py` records, for each PR, one comment that starts with
|
|
45
|
+
`<!-- ai-provenance:v1 -->` and holds a JSON record. It is computed on the
|
|
46
|
+
maintainer's machine, where the transcripts are. `/work-issue` step 8 runs it,
|
|
47
|
+
and a `SessionEnd` hook in `.claude/settings.json` runs it again in the
|
|
48
|
+
background when a session closes.
|
|
49
|
+
|
|
50
|
+
**Sessions → PRs.** A session counts for a PR if it created the PR (a `pr-link`
|
|
51
|
+
record), worked on its head branch, or is named in the PR body. Within a
|
|
52
|
+
session, a transcript record counts for the PR whose head branch it was on.
|
|
53
|
+
Otherwise it counts for the first PR the session linked at or after it, or the
|
|
54
|
+
last one if it came after all of them.
|
|
55
|
+
|
|
56
|
+
**Human steering**, per PR:
|
|
57
|
+
|
|
58
|
+
- *messages*: user records with `origin.kind == "human"`, excluding interrupts. A
|
|
59
|
+
slash command counts as the `/name args` that was typed.
|
|
60
|
+
- *words* and *characters* of those messages, plus words typed into
|
|
61
|
+
AskUserQuestion answers. Picking an offered option counts as an *answer*, not
|
|
62
|
+
as words.
|
|
63
|
+
- *active time*: for each message, the time since Claude's previous output; for
|
|
64
|
+
each answer, the time since the question. Each is capped at **15 minutes**, so
|
|
65
|
+
breaks don't count. A session's first message counts zero.
|
|
66
|
+
- *wall clock*: the span of the session's records that count for the PR.
|
|
67
|
+
- *messages after approval*: messages after the first reviewer hand-back with
|
|
68
|
+
verdict APPROVE.
|
|
69
|
+
|
|
70
|
+
**Agents**, per PR: models, effort levels and tokens (input including cache
|
|
71
|
+
reads and writes; output), deduplicated per API response, from the main and
|
|
72
|
+
subagent transcripts; agent time as the length of the union of the intervals
|
|
73
|
+
`[t − durationMs, t]` over the `turn_duration` records (within one prompt the
|
|
74
|
+
value is sometimes cumulative and sometimes per turn, and the union is right in
|
|
75
|
+
both cases); reviewer verdicts.
|
|
76
|
+
|
|
77
|
+
**Line authorship.** Each commit in `merge-base..head` is *ai* if an agent's
|
|
78
|
+
Bash output showed git's `[branch sha]` summary for it, or if its **author
|
|
79
|
+
date** falls within one of the agents' Bash calls (±2 s). The author date
|
|
80
|
+
survives rebases such as GitHub's "Update branch". Every other commit is
|
|
81
|
+
*human*. Each non-blank line the PR adds is blamed over that range. A line from
|
|
82
|
+
an *ai* commit is `ai`. A line from a *human* commit is `ai_edited` if that
|
|
83
|
+
commit's hunk replaced lines an *ai* commit had added, and otherwise `human`.
|
|
84
|
+
Lines are split into `src/`, `tests/`, `docs/` and other. The **coverage rule**
|
|
85
|
+
follows: whatever no transcript shows an agent doing counts as human, so
|
|
86
|
+
AI-written code must enter only through Claude Code (already a `CLAUDE.md`
|
|
87
|
+
rule).
|
|
88
|
+
|
|
89
|
+
Re-running keeps what an earlier run recorded: sessions whose transcripts have
|
|
90
|
+
expired, and commits already classified *ai*.
|
|
91
|
+
|
|
92
|
+
**Publishing.** The `Stats` workflow (on push to `main`, nightly, and on
|
|
93
|
+
demand) reads every merged PR's provenance comment, plus GitHub's data: reviewer
|
|
94
|
+
rounds and verdicts from the `Automated review (round N)` comments, formal
|
|
95
|
+
reviews, line comments, commits, time open, and which closed `difficulty: max`
|
|
96
|
+
issues human-written PRs closed. It writes `ai-stats.json` to the orphan `stats`
|
|
97
|
+
branch, the same pattern as the coverage badge. A PR whose final head differs
|
|
98
|
+
from the head its record was computed at is flagged `stale`. Each row also
|
|
99
|
+
counts the PR's commits by class, so human commits pushed to agent PRs are
|
|
100
|
+
published. Only comments by the repository owner (`authorAssociation`
|
|
101
|
+
`OWNER`) are read, and a malformed record is ignored, so other people's
|
|
102
|
+
comments can't forge or break the numbers. The Read the Docs
|
|
103
|
+
build renders the JSON on the *AI provenance* page. Nothing from the transcripts
|
|
104
|
+
is published except these numbers.
|
|
105
|
+
|
|
106
|
+
**Test strength.** mutmut runs nightly in the same workflow on
|
|
107
|
+
`src/treegraphduals/core` and `timeseries` (plotting code is excluded). The
|
|
108
|
+
score, (killed + timeout + segfault) / (those + survived + suspicious +
|
|
109
|
+
no tests), is published with the stats. Deterministic Hypothesis in CI stays
|
|
110
|
+
with #51.
|
|
111
|
+
|
|
112
|
+
## Consequences
|
|
113
|
+
|
|
114
|
+
- Every agent PR gets a provenance comment. Human PRs without one count as
|
|
115
|
+
human when their AI use section says "None: written by a human".
|
|
116
|
+
- PRs merged before this lands can be backfilled with `collect <pr>` while their
|
|
117
|
+
transcripts still exist (30 days).
|
|
118
|
+
- Known gaps: a human edit that an agent commits along with its own, a human
|
|
119
|
+
amending an agent's commit, or a human commit authored while an agent's Bash
|
|
120
|
+
call (such as a long test run) was running in a session on the same PR, counts
|
|
121
|
+
as AI. Code pasted from other AI chats
|
|
122
|
+
counts as human. Effort in sessions that never touch a PR (planning, triage)
|
|
123
|
+
isn't attributed to any PR.
|
|
124
|
+
- Formal approval records stay impossible (decision b). The review metrics are
|
|
125
|
+
the reviewer agent's rounds and the maintainer's messages after its approval.
|
|
126
|
+
- #72's page builds on the generated statistics and this ADR.
|