groupoid 0.1.0.dev6__tar.gz → 0.1.0.dev8__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 (85) hide show
  1. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/dependabot.yml +0 -1
  2. groupoid-0.1.0.dev8/.github/workflows/artifact-transfer.yml +46 -0
  3. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/workflows/ci.yml +43 -38
  4. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/workflows/docs-deploy.yml +1 -2
  5. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/workflows/release.yml +18 -12
  6. groupoid-0.1.0.dev8/.zenodo.json +34 -0
  7. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/CHANGELOG.md +49 -1
  8. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/CITATION.cff +2 -1
  9. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/CONTRIBUTING.md +8 -6
  10. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/LIMITATIONS.md +18 -7
  11. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/PKG-INFO +12 -9
  12. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/README.md +10 -5
  13. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/REPRODUCE.md +2 -1
  14. groupoid-0.1.0.dev8/SECURITY.md +31 -0
  15. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/STATUS.md +7 -5
  16. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/codemeta.json +1 -1
  17. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/docs/index.md +1 -1
  18. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/docs/quickstart.md +2 -1
  19. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/__init__.py +1 -1
  20. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/persistence.py +25 -12
  21. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid.egg-info/PKG-INFO +12 -9
  22. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid.egg-info/SOURCES.txt +1 -1
  23. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid.egg-info/requires.txt +0 -1
  24. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/pyproject.toml +7 -7
  25. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/scripts/reproduce_sheaf_laplacian.py +1 -1
  26. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_persistence.py +22 -10
  27. groupoid-0.1.0.dev6/.github/scripts/install_torch_cpu.sh +0 -25
  28. groupoid-0.1.0.dev6/.zenodo.json +0 -39
  29. groupoid-0.1.0.dev6/SECURITY.md +0 -43
  30. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  31. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  32. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/ISSUE_TEMPLATE/mathematical_correction.md +0 -0
  33. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.github/workflows/auto-tag-release.yml +0 -0
  34. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.gitignore +0 -0
  35. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/.pre-commit-config.yaml +0 -0
  36. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/CORRECTION_NOTICE.md +0 -0
  37. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/LICENSE +0 -0
  38. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/PROBLEMS.md +0 -0
  39. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/benchmarks/__init__.py +0 -0
  40. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/benchmarks/conftest.py +0 -0
  41. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/benchmarks/test_karcher_benchmark.py +0 -0
  42. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/docs/api.md +0 -0
  43. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/docs/overrides/404.html +0 -0
  44. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/docs/theory.md +0 -0
  45. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/examples/README.md +0 -0
  46. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/examples/__init__.py +0 -0
  47. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/examples/synthetic_curve_demo.py +0 -0
  48. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/experiments/PREREGISTRATION.md +0 -0
  49. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/experiments/README.md +0 -0
  50. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/experiments/RESULTS.md +0 -0
  51. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/experiments/analyze.py +0 -0
  52. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/experiments/results.json +0 -0
  53. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/experiments/run_benchmark.py +0 -0
  54. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/aggregation.py +0 -0
  55. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/cohomology.py +0 -0
  56. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/groupoid.py +0 -0
  57. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/laplacian.py +0 -0
  58. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/manifold.py +0 -0
  59. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/optimizer.py +0 -0
  60. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/sheaf.py +0 -0
  61. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid/transport.py +0 -0
  62. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid.egg-info/dependency_links.txt +0 -0
  63. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/groupoid.egg-info/top_level.txt +0 -0
  64. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/mkdocs.yml +0 -0
  65. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/scripts/bind_release_sbom.py +0 -0
  66. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/setup.cfg +0 -0
  67. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/__init__.py +0 -0
  68. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_aggregation_disconnected.py +0 -0
  69. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_audit_regressions.py +0 -0
  70. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_cohomology_cocycle.py +0 -0
  71. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_coverage_gaps.py +0 -0
  72. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_defensive_paths.py +0 -0
  73. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_imports.py +0 -0
  74. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_integration.py +0 -0
  75. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_optimizer_validation.py +0 -0
  76. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_persistence_dimension_labels.py +0 -0
  77. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_pipeline_integration.py +0 -0
  78. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_release_sbom_binding.py +0 -0
  79. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_release_workflow_integrity.py +0 -0
  80. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_scientific_corrections.py +0 -0
  81. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_sheaf_laplacian.py +0 -0
  82. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_smoke.py +0 -0
  83. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/test_transport_validation.py +0 -0
  84. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/theory/__init__.py +0 -0
  85. {groupoid-0.1.0.dev6 → groupoid-0.1.0.dev8}/tests/theory/test_mathematical_invariants.py +0 -0
@@ -42,7 +42,6 @@ updates:
42
42
  # of one PR per package.
43
43
  runtime:
44
44
  patterns:
45
- - "torch"
46
45
  - "geomstats"
47
46
  - "pymanopt"
48
47
  - "ripser"
@@ -0,0 +1,46 @@
1
+ name: Artifact transfer integrity
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [".github/workflows/**"]
7
+ pull_request:
8
+ branches: [main]
9
+ paths: [".github/workflows/**"]
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ round-trip:
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 5
18
+ steps:
19
+ - name: Create fixture and independent expected checksum
20
+ shell: bash
21
+ run: |
22
+ set -euo pipefail
23
+ mkdir -p payload/nested
24
+ printf '%s\n' "$GITHUB_SHA" > payload/nested/fixture.txt
25
+ (cd payload && sha256sum nested/fixture.txt) > expected.sha256
26
+ - name: Upload fixture
27
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
28
+ with:
29
+ name: artifact-transfer-proof
30
+ path: payload/
31
+ if-no-files-found: error
32
+ retention-days: 1
33
+ - name: Remove original fixture before downloading
34
+ run: rm -r payload
35
+ - name: Download fixture
36
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
37
+ with:
38
+ name: artifact-transfer-proof
39
+ path: restored/
40
+ - name: Verify restored bytes and directory layout
41
+ shell: bash
42
+ run: |
43
+ set -euo pipefail
44
+ cd restored
45
+ test "$(find . -type f | wc -l)" -eq 1
46
+ sha256sum --check ../expected.sha256
@@ -14,7 +14,7 @@ jobs:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
16
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
17
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
18
18
  with:
19
19
  python-version: "3.11"
20
20
  - name: Install dependencies
@@ -24,8 +24,8 @@ jobs:
24
24
  # numpy / pydantic stubs (NDArray dtype args, the BaseModel base class).
25
25
  # Linting only against `ruff black mypy` left numpy/pydantic uninstalled,
26
26
  # so --strict raised disallow_subclassing_any on the BaseModel subclass.
27
- # Consistent with the test / security / docs jobs. CPU torch keeps it lean.
28
- .github/scripts/install_torch_cpu.sh
27
+ # All jobs now install the declared runtime dependencies, without
28
+ # the unused optional PyTorch backend.
29
29
  pip install -e ".[dev]"
30
30
  - name: Run ruff
31
31
  run: ruff check .
@@ -34,20 +34,44 @@ jobs:
34
34
  - name: Run mypy (strict)
35
35
  run: mypy --strict groupoid/ --ignore-missing-imports
36
36
 
37
+ # Retain the protected-main historical check name while making its semantics
38
+ # a negative compatibility test. It proves that Python 3.10 is REJECTED;
39
+ # supported functional tests run only on Python 3.11 and 3.12 above.
40
+ unsupported-python310:
41
+ name: test (3.10)
42
+ runs-on: ubuntu-latest
43
+ steps:
44
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
45
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
46
+ with:
47
+ python-version: "3.10"
48
+ - name: Verify unsupported interpreter is rejected by package metadata
49
+ run: |
50
+ set -euo pipefail
51
+ if python -m pip install --no-deps . > /tmp/unsupported-python.log 2>&1; then
52
+ cat /tmp/unsupported-python.log
53
+ echo "::error::Python 3.10 installation unexpectedly succeeded"
54
+ exit 1
55
+ fi
56
+ if ! grep -Eqi "requires a different Python|requires-python|requires Python" /tmp/unsupported-python.log; then
57
+ cat /tmp/unsupported-python.log
58
+ echo "::error::Installation failed for an unexpected reason"
59
+ exit 1
60
+ fi
61
+
37
62
  test:
38
63
  runs-on: ubuntu-latest
39
64
  strategy:
40
65
  matrix:
41
- python-version: ["3.10", "3.11", "3.12"]
66
+ python-version: ["3.11", "3.12"]
42
67
  steps:
43
68
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
44
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
69
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
45
70
  with:
46
71
  python-version: ${{ matrix.python-version }}
47
72
  - name: Install dependencies
48
73
  run: |
49
74
  python -m pip install --upgrade pip
50
- .github/scripts/install_torch_cpu.sh
51
75
  pip install -e ".[dev]"
52
76
  - name: Run tests with coverage gate
53
77
  run: >-
@@ -59,7 +83,7 @@ jobs:
59
83
  runs-on: ubuntu-latest
60
84
  steps:
61
85
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
62
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
86
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
63
87
  with:
64
88
  python-version: "3.11"
65
89
  - name: Install dependencies
@@ -68,42 +92,25 @@ jobs:
68
92
  # Install the project's real runtime closure so pip-audit scans the
69
93
  # packages GROUPOID actually depends on. The previous `safety` step
70
94
  # installed only the tools and never the project, so it audited the
71
- # wrong environment. CPU torch keeps the install lean.
72
- .github/scripts/install_torch_cpu.sh
95
+ # wrong environment. No unused PyTorch install is injected.
73
96
  pip install -e .
74
97
  pip install bandit pip-audit
75
- # Pin setuptools LAST. torch requires setuptools<82 and pulls in a
76
- # runner-bundled setuptools that can predate the PYSEC-2025-49 fix
77
- # (78.1.1); upgrading before `pip install -e .` is simply undone by torch.
78
- # The window >=78.1.1,<82 clears the advisory while staying
79
- # dependency-consistent with torch (verified: pip check clean).
80
- python -m pip install --upgrade "setuptools>=78.1.1,<82"
98
+ # Setuptools is build tooling, but remains in the audited CI environment.
99
+ # No torch<82 constraint remains; require the patched release.
100
+ python -m pip install --upgrade "setuptools>=84.1.0"
101
+ python -m pip check
81
102
  - name: Run bandit
82
103
  run: bandit -r groupoid/ -c pyproject.toml
83
104
  - name: Audit dependencies for known vulnerabilities (pip-audit)
84
- # pip-audit (OSV-backed, maintained) replaces the deprecated `safety check`
85
- # (deprecated in safety 3.x in favour of the account-gated `safety scan`).
86
- # HARD gate: any advisory with a fix version fails the job. Two documented
87
- # exceptions, both disclosed in SECURITY.md:
88
- # - CVE-2025-3000 (torch): no fixed release exists upstream, and OSV
89
- # expresses the affected range (listed versions up to v2.6.0) as a
90
- # Git/commit boundary pip-audit cannot version-resolve, so it
91
- # conservatively flags the installed torch. The torch.load / JIT
92
- # deserialization path is not exercised by GROUPOID. Revisit the ignore
93
- # once upstream publishes a fixed release.
94
- # - PYSEC-2026-3447 (CVE-2026-59890, setuptools <83): the fix (83.0.0) is
95
- # unreachable while torch requires setuptools<82 (see the install step
96
- # above). The flaw is a macOS-only (APFS/HFS+) Unicode-normalization
97
- # bypass of MANIFEST.in exclude rules when building sdists; all CI and
98
- # release builds run on ubuntu-latest. Drop the ignore once torch
99
- # permits setuptools>=83.
100
- run: pip-audit --ignore-vuln CVE-2025-3000 --ignore-vuln PYSEC-2026-3447
105
+ # Hard gate on the installed runtime and audit-tool closure.
106
+ # No CVE/PYSEC ignore flags or waived vulnerable packages.
107
+ run: pip-audit
101
108
 
102
109
  pre-commit:
103
110
  runs-on: ubuntu-latest
104
111
  steps:
105
112
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
106
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
113
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
107
114
  with:
108
115
  python-version: "3.11"
109
116
  - name: Install pre-commit
@@ -115,13 +122,12 @@ jobs:
115
122
  runs-on: ubuntu-latest
116
123
  steps:
117
124
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
118
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
125
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
119
126
  with:
120
127
  python-version: "3.11"
121
128
  - name: Install dependencies
122
129
  run: |
123
130
  python -m pip install --upgrade pip
124
- .github/scripts/install_torch_cpu.sh
125
131
  pip install -e ".[dev]"
126
132
  # Benchmarks run for information only. There is no regression gate:
127
133
  # timing baselines recorded on one machine are not comparable to a
@@ -135,7 +141,7 @@ jobs:
135
141
  pytest benchmarks/ -v --benchmark-json=benchmark_results.json || true
136
142
  - name: Upload benchmark results
137
143
  if: always()
138
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
144
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
139
145
  with:
140
146
  name: benchmark-results
141
147
  path: benchmark_results.json
@@ -145,13 +151,12 @@ jobs:
145
151
  runs-on: ubuntu-latest
146
152
  steps:
147
153
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
148
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
154
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
149
155
  with:
150
156
  python-version: "3.11"
151
157
  - name: Install dependencies
152
158
  run: |
153
159
  python -m pip install --upgrade pip
154
- .github/scripts/install_torch_cpu.sh
155
160
  pip install -e ".[docs]"
156
161
  - name: Build docs
157
162
  run: mkdocs build --strict
@@ -17,13 +17,12 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
19
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
20
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
21
21
  with:
22
22
  python-version: "3.11"
23
23
  - name: Install dependencies
24
24
  run: |
25
25
  python -m pip install --upgrade pip
26
- .github/scripts/install_torch_cpu.sh
27
26
  pip install -e ".[docs]"
28
27
  - name: Build docs
29
28
  run: mkdocs build --strict
@@ -145,7 +145,7 @@ jobs:
145
145
  exit 1
146
146
  fi
147
147
  echo "release target ${RELEASE_TARGET} matches pyproject version ${version}"
148
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
148
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
149
149
  with:
150
150
  python-version: "3.12"
151
151
  cache: pip
@@ -283,7 +283,7 @@ jobs:
283
283
  with:
284
284
  subject-path: "dist/*"
285
285
  - name: Upload dist artifacts
286
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
286
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
287
287
  with:
288
288
  name: release-dist
289
289
  path: dist/
@@ -300,11 +300,11 @@ jobs:
300
300
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
301
301
  with:
302
302
  ref: ${{ github.event.inputs.ref || github.ref }}
303
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
303
+ - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
304
304
  with:
305
305
  python-version: "3.12"
306
306
  cache: pip
307
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
307
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
308
308
  with:
309
309
  name: release-dist
310
310
  path: dist
@@ -347,7 +347,7 @@ jobs:
347
347
  fi
348
348
  python scripts/bind_release_sbom.py --verify-only --sbom sbom.cdx.json --wheel "${wheels[0]}"
349
349
  - name: Upload SBOM artifact
350
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
350
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
351
351
  with:
352
352
  name: release-sbom
353
353
  path: sbom.cdx.json
@@ -365,11 +365,11 @@ jobs:
365
365
  id-token: write # attestation OIDC
366
366
  attestations: write # actions/attest
367
367
  steps:
368
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
368
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
369
369
  with:
370
370
  name: release-dist
371
371
  path: dist
372
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
372
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
373
373
  with:
374
374
  name: release-sbom
375
375
  path: .
@@ -407,7 +407,7 @@ jobs:
407
407
  name: pypi
408
408
  url: https://pypi.org/project/groupoid/
409
409
  steps:
410
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
410
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
411
411
  with:
412
412
  name: release-dist
413
413
  path: dist
@@ -479,7 +479,7 @@ jobs:
479
479
  # step below still runs, so COMPLETE still proves PyPI is correct.
480
480
  - name: Publish to PyPI (Trusted Publishing, OIDC)
481
481
  if: needs.build.outputs.adopted != 'true'
482
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
482
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
483
483
  with:
484
484
  packages-dir: dist
485
485
  print-hash: true
@@ -501,8 +501,14 @@ jobs:
501
501
  import hashlib, json, pathlib, sys, time, urllib.error, urllib.request
502
502
 
503
503
  dist = pathlib.Path("dist")
504
+ # PEP 740 .publish.attestation sidecars may be written to dist/ by
505
+ # the PyPI publishing action. They are attestations attached to the
506
+ # distributions, not separate package files in PyPI JSON urls.
507
+ # Compare every actual wheel/sdist byte without treating sidecars
508
+ # as missing distributions.
504
509
  local = {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
505
- for p in sorted(dist.iterdir()) if p.is_file()}
510
+ for p in sorted(dist.iterdir())
511
+ if p.is_file() and p.name.endswith((".whl", ".tar.gz"))}
506
512
  wheel = next(n for n in local if n.endswith(".whl"))
507
513
  project, version = wheel.split("-")[0], wheel.split("-")[1]
508
514
  url = f"https://pypi.org/pypi/{project}/{version}/json"
@@ -565,11 +571,11 @@ jobs:
565
571
  id-token: write # Sigstore signing OIDC
566
572
  steps:
567
573
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
568
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
574
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
569
575
  with:
570
576
  name: release-dist
571
577
  path: dist
572
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
578
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
573
579
  with:
574
580
  name: release-sbom
575
581
  path: .
@@ -0,0 +1,34 @@
1
+ {
2
+ "upload_type": "software",
3
+ "title": "GROUPOID: Groupoid-based aggregation for federated learning on Riemannian manifolds",
4
+ "version": "0.1.0.dev8",
5
+ "description": "<p>Pre-alpha research prototype exploring groupoid-based aggregation for federated learning on Riemannian manifolds. The supported point-valued aggregation path uses explicit invertible point actions, a cycle-basis holonomy defect diagnostic, the cellular sheaf Laplacian, and Karcher mean aggregation via geomstats. Tangent-vector parallel transport is provided separately through Schild and pole ladder utilities.</p><p><strong>Compatibility release v0.1.0.dev8:</strong> Python 3.10 support retired following upstream EOL on 2026-10-01. The supported runtime is Python 3.11-3.12; installation on 3.10 now fails closed via Requires-Python. No scientific algorithm or historical benchmark changes.</p><p><strong>Corrective release v0.1.0.dev7:</strong> the finite H0 bottleneck distance to an empty diagram is now half the longest unmatched interval, rather than an incorrect zero (both comparison directions tested). The unused mandatory PyTorch dependency was removed, reducing installation and audit complexity, and CI dependency scanning now runs without vulnerability exceptions. Pre-alpha research maturity, known limits on the H0-only diagnostic, and historical experiment results are unchanged. Existing environments may retain PyTorch after an upgrade.</p><p><strong>Library correction in v0.1.0.dev6:</strong> in v0.1.0.dev5 and earlier, the pole ladder used a step schedule that did not reach the endpoint for more than one rung, and the function named schild_ladder was a pole-ladder variant rather than Schild's construction; tangent-vector transport results from those versions with n_rungs greater than 1, including the default two-rung tangent transport operator, are incorrect. v0.1.0.dev6 corrects both ladders and adds input validation for unknown transport methods, n_rungs below 1, negative, non-finite or zero-sum aggregation weights, and non-finite holonomy maps. The preregistered benchmark uses explicit transport maps, not the ladders; its stored results are unchanged and were reproduced bit-for-bit under the dev6 candidate. The project remains a pre-alpha research prototype.</p><p><strong>Scientific correction in v0.1.0.dev5:</strong> earlier releases described the cycle-basis holonomy statistic as a first-cohomology (H^1) norm. The implementation actually computes the maximum Frobenius deviation of holonomy from identity over the particular NetworkX cycle basis. Exact zero has a flatness interpretation under explicit connectedness, completeness, invertibility, reciprocal opposite-edge registration when both directions are supplied, and complete evaluation of every cycle emitted by the NetworkX cycle-basis routine (an argument specific to the NetworkX Paton implementation exercised here, not to arbitrary cycle bases), but the nonzero magnitude is basis-dependent and is not invariant under arbitrary invertible changes of frame. The stored preregistered benchmark values are unchanged; the historical H2 rho and permutation p-value are reinterpreted as statistics for this fixed cycle-basis defect, not for a canonical H^1 norm. This version also withdraws register_transport_from_points from the supported point-valued aggregation path: its ambient square operator is assembled from transported tangent vectors and does not generally define the invertible manifold point action that aggregation requires. Tangent-vector ladder validation remains supported. The ambient tangent-matrix helper is restricted to vector-shaped point representations. Direct registered matrices are supported only under the explicit point-action contract documented in the repository, including reciprocal opposite arrows when both orientations are stored. See CORRECTION_NOTICE.md for the full correction record.</p><p>The earlier v0.1.0.dev0 version DOI 10.5281/zenodo.20563975 remains superseded and must not be cited for results. Exact historical versions should be cited by their version-specific DOI; project-level DOI metadata is maintained through the Zenodo version family.</p>",
6
+ "creators": [
7
+ {
8
+ "name": "Maniches, Santiago",
9
+ "affiliation": "TOPOLOGICA LLC",
10
+ "orcid": "0009-0005-6480-1987"
11
+ }
12
+ ],
13
+ "license": "Apache-2.0",
14
+ "keywords": [
15
+ "federated learning",
16
+ "Riemannian geometry",
17
+ "groupoid",
18
+ "sheaf theory",
19
+ "holonomy",
20
+ "topological data analysis"
21
+ ],
22
+ "related_identifiers": [
23
+ {
24
+ "identifier": "10.5281/zenodo.20563974",
25
+ "relation": "isVersionOf",
26
+ "scheme": "doi"
27
+ },
28
+ {
29
+ "identifier": "https://github.com/smaniches/GROUPOID",
30
+ "relation": "isSupplementTo",
31
+ "scheme": "url"
32
+ }
33
+ ]
34
+ }
@@ -8,6 +8,53 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.1.0.dev8] - 2026-10-30
12
+
13
+ ### Changed
14
+
15
+ - **Retired end-of-life Python 3.10 support.** Python 3.11 and 3.12 are the
16
+ supported and tested interpreter versions; `Requires-Python`, package
17
+ classifiers, installation instructions, test matrix, and citation metadata
18
+ now agree. Python 3.10 reached upstream end of life on October 1, 2026.
19
+ Existing users of Python 3.10 should migrate to 3.11 or 3.12 before
20
+ upgrading this package. Python 3.13+ remains excluded for the separately
21
+ documented numerical dependency constraints.
22
+ - This release contains no algorithm or scientific-method changes; historical
23
+ experimental artifacts and scientific qualification statements are unchanged.
24
+
25
+ ## [0.1.0.dev7] - 2026-10-29
26
+
27
+ Pre-alpha corrective release distributing the October 29 fixes already merged
28
+ on `main` (#77, #78 and #81). No new experiments, measured federated-learning
29
+ performance gains, or general convergence claims are introduced. Archived
30
+ preregistration and historical benchmark outputs remain unchanged.
31
+
32
+ ### Fixed
33
+
34
+ - **Finite H0 bottleneck versus an empty diagram (#77).** The previous
35
+ `track_divergence` fallback returned `0.0` when either finite H0 diagram
36
+ was empty. The mathematically correct cost is half the largest unmatched
37
+ persistence interval. For a two-point cloud separated by 4 versus a
38
+ singleton, the result is now **2.0** in either direction, not zero;
39
+ empty-versus-empty remains zero. Regression tests cover both directions.
40
+ The metric remains an H0-only geometric diagnostic, **not** a certificate
41
+ of federated model convergence or predictive stability.
42
+
43
+ ### Changed
44
+
45
+ - **Removed an unused mandatory dependency (#81).** `torch` was declared as
46
+ required but is not imported by the implemented library. New installations
47
+ no longer require PyTorch or its dependency graph. The security CI installs
48
+ the actual supported runtime closure, requires patched `setuptools`, checks
49
+ `pip check`, and runs `pip-audit` without advisory ignores. The full
50
+ Python 3.10–3.12 tests, docs, and code-quality checks passed on `main`.
51
+ **Compatibility note:** PyTorch is no longer installed automatically;
52
+ callers requiring it for their own workflows must install it separately.
53
+ Upgrading an existing environment does not uninstall previously installed
54
+ PyTorch or other packages; independently audit those environments.
55
+ - Updated SHA-pinned `actions/setup-python` and PyPI publishing action (#78).
56
+ No algorithmic behavior was changed by this CI maintenance.
57
+
11
58
  ## [0.1.0.dev6]
12
59
 
13
60
  Pre-alpha development release distributing the transport and validation fixes
@@ -268,7 +315,8 @@ Date shown is the tag commit (`v0.1.0.dev0` -> 2a02954). `CITATION.cff` records
268
315
  `date-released: 2026-05-25`, when the core implementation landed; the tag was
269
316
  later placed on the metadata commit.
270
317
 
271
- [Unreleased]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev6...HEAD
318
+ [Unreleased]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev7...HEAD
319
+ [0.1.0.dev7]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev6...v0.1.0.dev7
272
320
  [0.1.0.dev6]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev5...v0.1.0.dev6
273
321
  [0.1.0.dev5]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev4...v0.1.0.dev5
274
322
  [0.1.0.dev4]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev2...v0.1.0.dev4
@@ -2,7 +2,8 @@ cff-version: 1.2.0
2
2
  message: "If you use this software, please cite the Concept DOI below so the citation resolves to the latest version."
3
3
  type: software
4
4
  title: "GROUPOID: Groupoid-based aggregation for federated learning on Riemannian manifolds"
5
- version: 0.1.0.dev6
5
+ version: 0.1.0.dev8
6
+ date-released: 2026-10-30
6
7
  license: Apache-2.0
7
8
  repository-code: "https://github.com/smaniches/GROUPOID"
8
9
  doi: 10.5281/zenodo.20563974
@@ -80,18 +80,20 @@ please open an issue using the "Mathematical Correction" template.
80
80
  1. Open a release-cut PR that bumps the version across every metadata
81
81
  file that carries it: `pyproject.toml`, `groupoid/__init__.py`,
82
82
  `codemeta.json`, `CITATION.cff` (version and `date-released`),
83
- `STATUS.md` (both mentions), and `.zenodo.json` (version, the erratum
84
- description, and `isNewVersionOf` advanced to the prior version's
85
- Zenodo DOI). Roll `CHANGELOG.md`'s `Unreleased` section into a new
86
- version heading with compare links. See PRs #40 or #48 for the
83
+ `STATUS.md` (version), and `.zenodo.json` (version, the correction
84
+ description, and its concept DOI `isVersionOf` relationship). Only include
85
+ `isNewVersionOf` when the immediately preceding record DOI has been
86
+ independently verified; never inherit a stale predecessor. Roll
87
+ `CHANGELOG.md`'s `Unreleased` section into a new version heading with
88
+ compare links. See PRs #40 or #48 for the
87
89
  template.
88
90
  2. Merge the PR to `main`.
89
91
 
90
92
  That's it — merging is the release trigger, split across two workflows
91
93
  with one job each:
92
94
 
93
- - `.github/workflows/auto-tag-release.yml` runs on every push to `main`
94
- that touches `pyproject.toml`. It reads the version out of
95
+ - `.github/workflows/auto-tag-release.yml` runs after successful CI for a
96
+ push to the **current** `main` commit. It reads the version out of
95
97
  `pyproject.toml`, and if no `v<version>` tag exists yet, it creates and
96
98
  pushes one (as `github-actions[bot]`) after checking the version has a
97
99
  recognized format and that `CHANGELOG.md` documents it. A push with no
@@ -89,6 +89,15 @@ system. The following limitations apply.
89
89
  The informative loop signal lives in `max_persistence` / the finite
90
90
  bars; meaningful component counts require passing a finite
91
91
  `max_edge_length` between the intra- and inter-cluster scales.
92
+ - The opt-in `track_divergence` metric measures only changes in the
93
+ **finite H0** persistence diagram. It is invariant under translations,
94
+ rotations, and other pairwise-distance-preserving changes, and ignores
95
+ changes confined to H1 or higher homology. Thus a zero bottleneck
96
+ does **not** demonstrate client-model convergence or predictive
97
+ stability. If one diagram has no finite H0 bars, its distance to a
98
+ nonempty diagram is half the longest unmatched interval's lifetime,
99
+ not zero. Validation against model optimization would require
100
+ independent parameter-space or task-loss evidence.
92
101
  - No formal convergence rate analysis exists for the groupoid
93
102
  aggregation method.
94
103
 
@@ -98,17 +107,19 @@ system. The following limitations apply.
98
107
  - Requires `scipy < 1.16` for the same reason.
99
108
  - The `ripser` and `persim` packages may have build issues on
100
109
  some platforms (C++ compilation required).
101
- - `torch`, `pymanopt`, `POT`, and `einops` are declared as required
102
- runtime dependencies but are not imported by any current code path (the full
103
- test suite passes with all four uninstalled). Installing `groupoid` therefore
104
- pulls in packages, most notably `torch`, that the current code does not use.
105
- The `torch` advisory tracked in SECURITY.md applies to the installed
106
- dependency, not to any code path GROUPOID exercises.
110
+ - The former required `torch` dependency was removed because the current
111
+ implementation and entire documented test suite do not import it. It had
112
+ constrained `filelock` and `setuptools` to vulnerable versions; no current
113
+ GROUPOID code requires PyTorch. Future PyTorch functionality requires
114
+ a new separately tested dependency contract.
115
+ - `pymanopt`, `POT`, and `einops` are still declared as required runtime
116
+ dependencies but not imported in the current implementation. Their removal
117
+ is a separate compatibility change, not part of this security correction.
107
118
 
108
119
  ## Test coverage
109
120
 
110
121
  - Releases are gated on the repository's hard line and branch coverage checks
111
- on Python 3.10-3.12. Coverage is not validation: the scientific correction
122
+ on Python 3.11-3.12. Coverage is not validation: the scientific correction
112
123
  adds explicit tests for basis dependence, general non-orthogonal gauge
113
124
  dependence of the defect magnitude, the tangent/point representation
114
125
  boundary, and the supported SO(3) point-action round trip.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: groupoid
3
- Version: 0.1.0.dev6
3
+ Version: 0.1.0.dev8
4
4
  Summary: Research prototype: groupoid-based aggregation for federated learning on Riemannian manifolds
5
5
  Author-email: Santiago Maniches <santiago.maniches@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -13,12 +13,11 @@ Keywords: federated-learning,riemannian-geometry,groupoid,topology,sheaf-theory,
13
13
  Classifier: Development Status :: 2 - Pre-Alpha
14
14
  Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Topic :: Scientific/Engineering :: Mathematics
20
19
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
- Requires-Python: <3.13,>=3.10
20
+ Requires-Python: <3.13,>=3.11
22
21
  Description-Content-Type: text/markdown
23
22
  License-File: LICENSE
24
23
  Requires-Dist: geomstats>=0.7
@@ -30,7 +29,6 @@ Requires-Dist: networkx>=3.0
30
29
  Requires-Dist: einops>=0.7
31
30
  Requires-Dist: numpy<2.0,>=1.24
32
31
  Requires-Dist: scipy<1.16,>=1.10
33
- Requires-Dist: torch>=2.0
34
32
  Requires-Dist: pydantic>=2.0
35
33
  Requires-Dist: loguru>=0.7
36
34
  Provides-Extra: privacy
@@ -68,7 +66,7 @@ Dynamic: license-file
68
66
  [![CI](https://github.com/smaniches/GROUPOID/actions/workflows/ci.yml/badge.svg)](https://github.com/smaniches/GROUPOID/actions/workflows/ci.yml)
69
67
  [![PyPI](https://img.shields.io/pypi/v/groupoid.svg)](https://pypi.org/project/groupoid/)
70
68
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
71
- [![Python 3.10-3.12](https://img.shields.io/badge/python-3.10--3.12-blue.svg)](https://www.python.org/downloads/)
69
+ [![Python 3.11-3.12](https://img.shields.io/badge/python-3.11--3.12-blue.svg)](https://www.python.org/downloads/)
72
70
  [![Status](https://img.shields.io/badge/status-pre--alpha-orange.svg)](#status)
73
71
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20563974.svg)](https://doi.org/10.5281/zenodo.20563974)
74
72
 
@@ -139,8 +137,12 @@ integration tests:
139
137
  retains a homology-dimension label, and `track_divergence` compares H0
140
138
  against H0 only (it does not pool features across dimensions); this is
141
139
  verified against an independent minimum-spanning-tree reconstruction of
142
- the H0 diagram. The Betti numbers are degenerate under the default
143
- `thresh=inf` filtration; see [LIMITATIONS.md](LIMITATIONS.md).
140
+ the H0 diagram. A zero value is a geometric diagnostic, **not** a
141
+ certificate of client-model convergence: translations and H1-only
142
+ changes can leave it unchanged. A finite H0 diagram compared with
143
+ an empty one incurs the nonzero bottleneck-to-diagonal cost.
144
+ The Betti numbers are degenerate under the default `thresh=inf`
145
+ filtration; see [LIMITATIONS.md](LIMITATIONS.md).
144
146
 
145
147
  ## Implemented and validated, not yet integrated
146
148
 
@@ -201,7 +203,8 @@ hypothesis remains **unvalidated on real federated learning tasks** (see
201
203
 
202
204
  ## Installation
203
205
 
204
- Requires **Python 3.10, 3.11, or 3.12**. Python 3.13+ is not supported: the
206
+ Requires **Python 3.11 or 3.12**. Python 3.10 reached end of life on
207
+ October 1, 2026 and is no longer supported. Python 3.13+ is not supported: the
205
208
  `numpy<2.0` / `scipy<1.16` pins (needed for geomstats compatibility, see
206
209
  [LIMITATIONS.md](LIMITATIONS.md)) have no wheels there, so `pip` will refuse
207
210
  with a `Requires-Python` message rather than attempt a source build.
@@ -266,7 +269,7 @@ pytest tests/ -v
266
269
  ```
267
270
 
268
271
  The suite reaches 100% line and branch coverage of the `groupoid` package
269
- on Python 3.10-3.12, enforced in CI:
272
+ on Python 3.11-3.12, enforced in CI:
270
273
 
271
274
  ```bash
272
275
  pytest tests/ --cov=groupoid --cov-branch --cov-fail-under=100
@@ -3,7 +3,7 @@
3
3
  [![CI](https://github.com/smaniches/GROUPOID/actions/workflows/ci.yml/badge.svg)](https://github.com/smaniches/GROUPOID/actions/workflows/ci.yml)
4
4
  [![PyPI](https://img.shields.io/pypi/v/groupoid.svg)](https://pypi.org/project/groupoid/)
5
5
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
- [![Python 3.10-3.12](https://img.shields.io/badge/python-3.10--3.12-blue.svg)](https://www.python.org/downloads/)
6
+ [![Python 3.11-3.12](https://img.shields.io/badge/python-3.11--3.12-blue.svg)](https://www.python.org/downloads/)
7
7
  [![Status](https://img.shields.io/badge/status-pre--alpha-orange.svg)](#status)
8
8
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20563974.svg)](https://doi.org/10.5281/zenodo.20563974)
9
9
 
@@ -74,8 +74,12 @@ integration tests:
74
74
  retains a homology-dimension label, and `track_divergence` compares H0
75
75
  against H0 only (it does not pool features across dimensions); this is
76
76
  verified against an independent minimum-spanning-tree reconstruction of
77
- the H0 diagram. The Betti numbers are degenerate under the default
78
- `thresh=inf` filtration; see [LIMITATIONS.md](LIMITATIONS.md).
77
+ the H0 diagram. A zero value is a geometric diagnostic, **not** a
78
+ certificate of client-model convergence: translations and H1-only
79
+ changes can leave it unchanged. A finite H0 diagram compared with
80
+ an empty one incurs the nonzero bottleneck-to-diagonal cost.
81
+ The Betti numbers are degenerate under the default `thresh=inf`
82
+ filtration; see [LIMITATIONS.md](LIMITATIONS.md).
79
83
 
80
84
  ## Implemented and validated, not yet integrated
81
85
 
@@ -136,7 +140,8 @@ hypothesis remains **unvalidated on real federated learning tasks** (see
136
140
 
137
141
  ## Installation
138
142
 
139
- Requires **Python 3.10, 3.11, or 3.12**. Python 3.13+ is not supported: the
143
+ Requires **Python 3.11 or 3.12**. Python 3.10 reached end of life on
144
+ October 1, 2026 and is no longer supported. Python 3.13+ is not supported: the
140
145
  `numpy<2.0` / `scipy<1.16` pins (needed for geomstats compatibility, see
141
146
  [LIMITATIONS.md](LIMITATIONS.md)) have no wheels there, so `pip` will refuse
142
147
  with a `Requires-Python` message rather than attempt a source build.
@@ -201,7 +206,7 @@ pytest tests/ -v
201
206
  ```
202
207
 
203
208
  The suite reaches 100% line and branch coverage of the `groupoid` package
204
- on Python 3.10-3.12, enforced in CI:
209
+ on Python 3.11-3.12, enforced in CI:
205
210
 
206
211
  ```bash
207
212
  pytest tests/ --cov=groupoid --cov-branch --cov-fail-under=100