diag3d 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.
Files changed (185) hide show
  1. diag3d-0.2.0/.github/dependabot.yml +6 -0
  2. diag3d-0.2.0/.github/workflows/ci.yml +86 -0
  3. diag3d-0.2.0/.github/workflows/prepare-release.yml +83 -0
  4. diag3d-0.2.0/.github/workflows/release.yml +146 -0
  5. diag3d-0.2.0/.gitignore +32 -0
  6. diag3d-0.2.0/ARCHITECTURE.md +70 -0
  7. diag3d-0.2.0/BEFORE_AFTER.md +29 -0
  8. diag3d-0.2.0/BENCHMARK_RESULTS.md +72 -0
  9. diag3d-0.2.0/CHANGELOG.md +98 -0
  10. diag3d-0.2.0/CONTRIBUTING.md +5 -0
  11. diag3d-0.2.0/FLAGSHIP_DEMO.md +18 -0
  12. diag3d-0.2.0/IMPLEMENTATION_STATUS.md +54 -0
  13. diag3d-0.2.0/INDEPENDENT_BOOTSTRAP_RESULTS.md +22 -0
  14. diag3d-0.2.0/LICENSE +21 -0
  15. diag3d-0.2.0/MODEL_SPACE_ARCHITECTURE.md +74 -0
  16. diag3d-0.2.0/NESTED_REPAIR_RESULTS.md +36 -0
  17. diag3d-0.2.0/NULL_CALIBRATION_RESULTS.md +45 -0
  18. diag3d-0.2.0/PKG-INFO +327 -0
  19. diag3d-0.2.0/README.md +266 -0
  20. diag3d-0.2.0/SECURITY.md +3 -0
  21. diag3d-0.2.0/SOURCE_AUDIT.md +36 -0
  22. diag3d-0.2.0/THIRD_PARTY_NOTICES.md +38 -0
  23. diag3d-0.2.0/UX_SPEC.md +50 -0
  24. diag3d-0.2.0/VALIDATION_RESULTS.md +73 -0
  25. diag3d-0.2.0/VISUAL_EVIDENCE_REPORT.md +32 -0
  26. diag3d-0.2.0/docs/architecture.md +60 -0
  27. diag3d-0.2.0/docs/benchmarks.md +30 -0
  28. diag3d-0.2.0/docs/consolidation.md +38 -0
  29. diag3d-0.2.0/docs/dependencies.md +21 -0
  30. diag3d-0.2.0/docs/figures/concept.svg +284 -0
  31. diag3d-0.2.0/docs/glb_export.md +147 -0
  32. diag3d-0.2.0/docs/history/IMPLEMENTATION_STATUS-null-validation.md +39 -0
  33. diag3d-0.2.0/docs/history/README-null-validation.md +120 -0
  34. diag3d-0.2.0/docs/legacy/METHODS-v0.1.md +122 -0
  35. diag3d-0.2.0/docs/legacy/README-v0.1.md +281 -0
  36. diag3d-0.2.0/docs/legacy/VALIDATION-v0.1.md +74 -0
  37. diag3d-0.2.0/docs/lifecycle.md +333 -0
  38. diag3d-0.2.0/docs/lifecycle_audit.md +64 -0
  39. diag3d-0.2.0/docs/lifecycle_plan.md +92 -0
  40. diag3d-0.2.0/docs/limitations.md +14 -0
  41. diag3d-0.2.0/docs/model-space/00-research-explorer-before.png +0 -0
  42. diag3d-0.2.0/docs/model-space/01-data.png +0 -0
  43. diag3d-0.2.0/docs/model-space/05-failure.png +0 -0
  44. diag3d-0.2.0/docs/model-space/07-repair-after.png +0 -0
  45. diag3d-0.2.0/docs/model-space/08-classification.png +0 -0
  46. diag3d-0.2.0/docs/model-space/10-wine-pre-run.png +0 -0
  47. diag3d-0.2.0/docs/packaging-validation.md +37 -0
  48. diag3d-0.2.0/docs/prior_art_and_novelty_questions.md +39 -0
  49. diag3d-0.2.0/docs/public_api.md +57 -0
  50. diag3d-0.2.0/docs/releasing.md +87 -0
  51. diag3d-0.2.0/docs/research_method.md +150 -0
  52. diag3d-0.2.0/docs/v01_inventory.json +171 -0
  53. diag3d-0.2.0/docs/validation/benchmark_results.json +1800 -0
  54. diag3d-0.2.0/docs/validation/initial_core_only_results.json +1800 -0
  55. diag3d-0.2.0/docs/validation/model_space_browser.json +60 -0
  56. diag3d-0.2.0/docs/validation/model_space_handoff.json +19 -0
  57. diag3d-0.2.0/docs/validation/model_space_performance.json +17 -0
  58. diag3d-0.2.0/docs/validation/model_space_primary.json +342 -0
  59. diag3d-0.2.0/docs/validation/null_validation_calibration_sensitivity.json +25 -0
  60. diag3d-0.2.0/docs/validation/null_validation_experiment_summary.json +459 -0
  61. diag3d-0.2.0/docs/validation/null_validation_plugin_null_summary.json +7 -0
  62. diag3d-0.2.0/docs/validation/null_validation_search_precision.json +13 -0
  63. diag3d-0.2.0/docs/validation/performance_results.json +12 -0
  64. diag3d-0.2.0/docs/validation/sensitivity_results.json +450 -0
  65. diag3d-0.2.0/examples/DATASETS.md +32 -0
  66. diag3d-0.2.0/examples/MODEL_SPACE_DATASETS.md +15 -0
  67. diag3d-0.2.0/examples/export_glb.py +52 -0
  68. diag3d-0.2.0/examples/export_previews.py +61 -0
  69. diag3d-0.2.0/examples/fetch_industry_data.py +156 -0
  70. diag3d-0.2.0/examples/generate_data.py +55 -0
  71. diag3d-0.2.0/examples/generate_gallery.py +84 -0
  72. diag3d-0.2.0/examples/generate_industry_gallery.py +143 -0
  73. diag3d-0.2.0/examples/industry_catalog.py +99 -0
  74. diag3d-0.2.0/examples/lifecycle_1_preflight.py +59 -0
  75. diag3d-0.2.0/examples/lifecycle_2_training.py +125 -0
  76. diag3d-0.2.0/examples/lifecycle_3_posthoc.py +104 -0
  77. diag3d-0.2.0/examples/model_space_demos.py +147 -0
  78. diag3d-0.2.0/examples/python_api.py +31 -0
  79. diag3d-0.2.0/examples/static_previews.py +153 -0
  80. diag3d-0.2.0/experiments/calibration_sensitivity.py +139 -0
  81. diag3d-0.2.0/experiments/make_evidence_report.py +273 -0
  82. diag3d-0.2.0/experiments/make_figures.py +12 -0
  83. diag3d-0.2.0/experiments/null_validation.py +419 -0
  84. diag3d-0.2.0/experiments/performance.py +50 -0
  85. diag3d-0.2.0/experiments/plot_acceptance.py +80 -0
  86. diag3d-0.2.0/experiments/plugin_null_pilot.py +81 -0
  87. diag3d-0.2.0/experiments/refresh_refit.py +59 -0
  88. diag3d-0.2.0/experiments/run_benchmarks.py +60 -0
  89. diag3d-0.2.0/experiments/search_precision.py +103 -0
  90. diag3d-0.2.0/experiments/sensitivity.py +65 -0
  91. diag3d-0.2.0/pyproject.toml +72 -0
  92. diag3d-0.2.0/src/diag3d/__init__.py +67 -0
  93. diag3d-0.2.0/src/diag3d/__main__.py +3 -0
  94. diag3d-0.2.0/src/diag3d/_version.py +1 -0
  95. diag3d-0.2.0/src/diag3d/baselines.py +246 -0
  96. diag3d-0.2.0/src/diag3d/cli.py +849 -0
  97. diag3d-0.2.0/src/diag3d/config.py +27 -0
  98. diag3d-0.2.0/src/diag3d/dataset.py +160 -0
  99. diag3d-0.2.0/src/diag3d/diagnostics.py +283 -0
  100. diag3d-0.2.0/src/diag3d/lifecycle/__init__.py +39 -0
  101. diag3d-0.2.0/src/diag3d/lifecycle/figures.py +103 -0
  102. diag3d-0.2.0/src/diag3d/lifecycle/findings.py +128 -0
  103. diag3d-0.2.0/src/diag3d/lifecycle/investigate.py +347 -0
  104. diag3d-0.2.0/src/diag3d/lifecycle/monitor.py +720 -0
  105. diag3d-0.2.0/src/diag3d/lifecycle/posthoc.py +984 -0
  106. diag3d-0.2.0/src/diag3d/lifecycle/predictions.py +529 -0
  107. diag3d-0.2.0/src/diag3d/lifecycle/preflight.py +783 -0
  108. diag3d-0.2.0/src/diag3d/lifecycle/report.py +199 -0
  109. diag3d-0.2.0/src/diag3d/lifecycle/run.py +694 -0
  110. diag3d-0.2.0/src/diag3d/lifecycle/splits.py +289 -0
  111. diag3d-0.2.0/src/diag3d/lifecycle/workflow.py +121 -0
  112. diag3d-0.2.0/src/diag3d/model_space/__init__.py +6 -0
  113. diag3d-0.2.0/src/diag3d/model_space/adapters.py +138 -0
  114. diag3d-0.2.0/src/diag3d/model_space/assets/explorer.css +10 -0
  115. diag3d-0.2.0/src/diag3d/model_space/assets/explorer.html +29 -0
  116. diag3d-0.2.0/src/diag3d/model_space/assets/explorer.js +298 -0
  117. diag3d-0.2.0/src/diag3d/model_space/diagnostics.py +143 -0
  118. diag3d-0.2.0/src/diag3d/model_space/glb.py +605 -0
  119. diag3d-0.2.0/src/diag3d/model_space/session.py +498 -0
  120. diag3d-0.2.0/src/diag3d/modeling.py +265 -0
  121. diag3d-0.2.0/src/diag3d/profiling.py +137 -0
  122. diag3d-0.2.0/src/diag3d/relationships.py +166 -0
  123. diag3d-0.2.0/src/diag3d/reporting.py +566 -0
  124. diag3d-0.2.0/src/diag3d/research/__init__.py +19 -0
  125. diag3d-0.2.0/src/diag3d/research/analyzer.py +287 -0
  126. diag3d-0.2.0/src/diag3d/research/benchmarks.py +196 -0
  127. diag3d-0.2.0/src/diag3d/research/calibration.py +298 -0
  128. diag3d-0.2.0/src/diag3d/research/config.py +94 -0
  129. diag3d-0.2.0/src/diag3d/research/diagnostics.py +158 -0
  130. diag3d-0.2.0/src/diag3d/research/discovery.py +155 -0
  131. diag3d-0.2.0/src/diag3d/research/explorer.py +523 -0
  132. diag3d-0.2.0/src/diag3d/research/fields.py +115 -0
  133. diag3d-0.2.0/src/diag3d/research/figures.py +1098 -0
  134. diag3d-0.2.0/src/diag3d/research/nested.py +457 -0
  135. diag3d-0.2.0/src/diag3d/research/nulls.py +73 -0
  136. diag3d-0.2.0/src/diag3d/research/oof.py +197 -0
  137. diag3d-0.2.0/src/diag3d/research/refit.py +220 -0
  138. diag3d-0.2.0/src/diag3d/research/repairs.py +205 -0
  139. diag3d-0.2.0/src/diag3d/research/stability.py +135 -0
  140. diag3d-0.2.0/src/diag3d/research/types.py +360 -0
  141. diag3d-0.2.0/src/diag3d/spatial.py +86 -0
  142. diag3d-0.2.0/src/diag3d/surfaces.py +94 -0
  143. diag3d-0.2.0/src/diag3d/utils.py +71 -0
  144. diag3d-0.2.0/src/diag3d/visualization.py +423 -0
  145. diag3d-0.2.0/tests/conftest.py +34 -0
  146. diag3d-0.2.0/tests/industry_runtime.mjs +45 -0
  147. diag3d-0.2.0/tests/lifecycle_data.py +51 -0
  148. diag3d-0.2.0/tests/model_space_runtime.cjs +91 -0
  149. diag3d-0.2.0/tests/report_runtime.mjs +60 -0
  150. diag3d-0.2.0/tests/research_runtime.mjs +21 -0
  151. diag3d-0.2.0/tests/test_baselines.py +118 -0
  152. diag3d-0.2.0/tests/test_dataset.py +100 -0
  153. diag3d-0.2.0/tests/test_diagnostics.py +62 -0
  154. diag3d-0.2.0/tests/test_edge_cases.py +61 -0
  155. diag3d-0.2.0/tests/test_glb.py +287 -0
  156. diag3d-0.2.0/tests/test_industry_gallery.py +99 -0
  157. diag3d-0.2.0/tests/test_lifecycle_audit.py +765 -0
  158. diag3d-0.2.0/tests/test_lifecycle_e2e.py +249 -0
  159. diag3d-0.2.0/tests/test_lifecycle_examples.py +47 -0
  160. diag3d-0.2.0/tests/test_lifecycle_membership.py +192 -0
  161. diag3d-0.2.0/tests/test_lifecycle_monitor.py +299 -0
  162. diag3d-0.2.0/tests/test_lifecycle_portability.py +84 -0
  163. diag3d-0.2.0/tests/test_lifecycle_posthoc.py +283 -0
  164. diag3d-0.2.0/tests/test_lifecycle_predictions.py +168 -0
  165. diag3d-0.2.0/tests/test_lifecycle_preflight.py +189 -0
  166. diag3d-0.2.0/tests/test_lifecycle_regressions.py +28 -0
  167. diag3d-0.2.0/tests/test_lifecycle_run.py +124 -0
  168. diag3d-0.2.0/tests/test_model_space.py +326 -0
  169. diag3d-0.2.0/tests/test_modeling.py +77 -0
  170. diag3d-0.2.0/tests/test_null_validation.py +337 -0
  171. diag3d-0.2.0/tests/test_public_api.py +71 -0
  172. diag3d-0.2.0/tests/test_release_gates.py +145 -0
  173. diag3d-0.2.0/tests/test_reporting_cli.py +128 -0
  174. diag3d-0.2.0/tests/test_research_engine.py +478 -0
  175. diag3d-0.2.0/tests/test_research_packaging.py +29 -0
  176. diag3d-0.2.0/tests/test_scientific_figures.py +60 -0
  177. diag3d-0.2.0/tests/test_visualizations.py +64 -0
  178. diag3d-0.2.0/tools/check_distribution.py +156 -0
  179. diag3d-0.2.0/tools/index_release.py +117 -0
  180. diag3d-0.2.0/tools/lifecycle_mutations.py +117 -0
  181. diag3d-0.2.0/tools/package_smoke.py +135 -0
  182. diag3d-0.2.0/tools/release.py +25 -0
  183. diag3d-0.2.0/tools/security_scan.py +95 -0
  184. diag3d-0.2.0/tools/test_distribution.py +41 -0
  185. diag3d-0.2.0/tools/validate_evidence.py +98 -0
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: monthly
@@ -0,0 +1,86 @@
1
+ name: Scientific tests and package
2
+ on:
3
+ push:
4
+ pull_request:
5
+ workflow_call:
6
+ inputs:
7
+ expected_tag:
8
+ type: string
9
+ default: ''
10
+ permissions:
11
+ contents: read
12
+ env:
13
+ OMP_NUM_THREADS: '1'
14
+ OPENBLAS_NUM_THREADS: '1'
15
+ POLARS_MAX_THREADS: '2'
16
+ PYTHONUTF8: '1'
17
+ jobs:
18
+ test:
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ python: ['3.11', '3.12', '3.13']
24
+ steps:
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+ with:
27
+ persist-credentials: false
28
+ fetch-depth: 0
29
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
30
+ with:
31
+ python-version: ${{ matrix.python }}
32
+ - run: python -m pip install '.[dev,gallery]'
33
+ - run: python -m compileall -q src
34
+ - run: python -m ruff check src tests experiments tools
35
+ - run: python -m pytest -q
36
+ - run: python tools/security_scan.py --history
37
+ - name: Installed wheel and sdist smoke test on this Python
38
+ run: |
39
+ python -m build --outdir "$RUNNER_TEMP/dist"
40
+ python tools/test_distribution.py "$RUNNER_TEMP"/dist/*
41
+ windows:
42
+ runs-on: windows-latest
43
+ env:
44
+ PYTHONUTF8: '0' # the default code page, so encoding regressions surface here
45
+ steps:
46
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
47
+ with:
48
+ persist-credentials: false
49
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
50
+ with:
51
+ python-version: '3.12'
52
+ - run: python -m pip install ".[dev,gallery]"
53
+ - run: python -m pytest -q
54
+ build:
55
+ needs: [test, windows]
56
+ runs-on: ubuntu-latest
57
+ steps:
58
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
59
+ with:
60
+ persist-credentials: false
61
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
62
+ with:
63
+ python-version: '3.12'
64
+ - run: python -m pip install 'build>=1.2,<2' 'twine>=6,<8'
65
+ - name: Build wheel from sdist with a reproducible timestamp
66
+ run: |
67
+ export SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)
68
+ python -m build
69
+ - run: python -m twine check --strict dist/*
70
+ - name: Validate distribution contents and version
71
+ env:
72
+ EXPECTED_TAG: ${{ inputs.expected_tag }}
73
+ run: |
74
+ if [ -n "$EXPECTED_TAG" ]; then
75
+ python tools/check_distribution.py --tag "$EXPECTED_TAG"
76
+ else
77
+ python tools/check_distribution.py
78
+ fi
79
+ - name: Analyze using clean wheel and sdist installations
80
+ run: python tools/test_distribution.py dist/*
81
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
82
+ with:
83
+ name: python-dist
84
+ path: dist/
85
+ if-no-files-found: error
86
+ retention-days: 30
@@ -0,0 +1,83 @@
1
+ name: Prepare release
2
+ # Creates the version tag and a DRAFT GitHub Release with notes taken from CHANGELOG.md.
3
+ # Nothing is published: publishing the draft (a deliberate human action) fires the
4
+ # Release workflow, which stages on TestPyPI, publishes to PyPI through trusted
5
+ # publishing and attaches the verified artifacts.
6
+ on:
7
+ workflow_dispatch:
8
+ inputs:
9
+ version:
10
+ description: 'Stable version to release, e.g. 0.2.0 (must equal src/diag3d/_version.py on main)'
11
+ type: string
12
+ required: true
13
+ permissions:
14
+ contents: read
15
+ env:
16
+ OMP_NUM_THREADS: '1'
17
+ OPENBLAS_NUM_THREADS: '1'
18
+ POLARS_MAX_THREADS: '2'
19
+ PYTHONUTF8: '1'
20
+ jobs:
21
+ prepare:
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: write
25
+ steps:
26
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
27
+ with:
28
+ persist-credentials: false
29
+ fetch-depth: 0
30
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
31
+ with:
32
+ python-version: '3.12'
33
+ - name: Require main, a stable version, a matching _version.py and a changelog entry
34
+ env:
35
+ VERSION: ${{ inputs.version }}
36
+ run: |
37
+ test "$GITHUB_REF" = "refs/heads/main"
38
+ [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
39
+ grep -qx "__version__ = \"$VERSION\"" src/diag3d/_version.py
40
+ grep -q "^## $VERSION " CHANGELOG.md
41
+ if git rev-parse -q --verify "refs/tags/v$VERSION" >/dev/null; then
42
+ echo "Tag v$VERSION already exists"; exit 1
43
+ fi
44
+ - run: python -m pip install ".[dev,gallery]"
45
+ - run: python -m compileall -q src
46
+ - run: python -m ruff check src tests experiments tools
47
+ - run: python -m pytest -q
48
+ - run: python tools/security_scan.py --history
49
+ - name: Build and audit the distributions
50
+ run: |
51
+ export SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)
52
+ python -m build
53
+ python -m twine check --strict dist/*
54
+ python tools/check_distribution.py --tag "v${{ inputs.version }}"
55
+ - name: Extract release notes from CHANGELOG.md
56
+ env:
57
+ VERSION: ${{ inputs.version }}
58
+ run: |
59
+ python - <<'EOF'
60
+ import os, re, pathlib
61
+ version = os.environ["VERSION"]
62
+ text = pathlib.Path("CHANGELOG.md").read_text(encoding="utf-8")
63
+ match = re.search(rf"^## {re.escape(version)}[^\n]*\n(.*?)(?=^## |\Z)", text, re.S | re.M)
64
+ body = match.group(1).strip() if match else ""
65
+ if not body:
66
+ raise SystemExit("No changelog section for this version")
67
+ notes = (f"{body}\n\n## Installation\n\n```bash\npip install diag3d=={version}\n"
68
+ "```\n\nmacOS: `brew tap falhezaimi/tap && brew install diag3d`\n\n"
69
+ "Wheel, sdist and SHA256SUMS are attached after PyPI publication is verified.\n")
70
+ pathlib.Path("release_notes.md").write_text(notes, encoding="utf-8")
71
+ EOF
72
+ - name: Create the annotated tag and a draft release
73
+ env:
74
+ GH_TOKEN: ${{ github.token }}
75
+ VERSION: ${{ inputs.version }}
76
+ run: |
77
+ git config user.name "github-actions[bot]"
78
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
79
+ git tag -a "v$VERSION" -m "Diag3D $VERSION"
80
+ git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/v$VERSION"
81
+ gh release create "v$VERSION" --draft --verify-tag --title "Diag3D $VERSION" \
82
+ --notes-file release_notes.md
83
+ echo "Draft release v$VERSION created; stage it with the Release workflow, then publish the draft."
@@ -0,0 +1,146 @@
1
+ name: Release
2
+ on:
3
+ workflow_dispatch:
4
+ inputs:
5
+ tag:
6
+ description: 'Approved stable tag to stage on TestPyPI, e.g. v0.2.0; select the same tag as ref'
7
+ type: string
8
+ required: true
9
+ release:
10
+ types: [published]
11
+ permissions:
12
+ contents: read
13
+ concurrency:
14
+ group: release-${{ github.event.release.tag_name || inputs.tag }}
15
+ cancel-in-progress: false
16
+ env:
17
+ OMP_NUM_THREADS: '1'
18
+ OPENBLAS_NUM_THREADS: '1'
19
+ POLARS_MAX_THREADS: '2'
20
+ jobs:
21
+ gate:
22
+ if: github.event_name == 'workflow_dispatch' || (!github.event.release.prerelease && !github.event.release.draft)
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Require an explicit stable version tag and matching ref
26
+ env:
27
+ TAG: ${{ github.event.release.tag_name || inputs.tag }}
28
+ run: |
29
+ [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
30
+ test "$GITHUB_REF" = "refs/tags/$TAG"
31
+ validate:
32
+ needs: gate
33
+ uses: ./.github/workflows/ci.yml
34
+ with:
35
+ expected_tag: ${{ github.event.release.tag_name || inputs.tag }}
36
+ staging-status:
37
+ needs: validate
38
+ runs-on: ubuntu-latest
39
+ outputs:
40
+ already_staged: ${{ steps.status.outputs.already_staged }}
41
+ steps:
42
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
43
+ with:
44
+ persist-credentials: false
45
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
46
+ with:
47
+ python-version: '3.12'
48
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
49
+ with:
50
+ name: python-dist
51
+ path: dist/
52
+ - id: status
53
+ run: python tools/index_release.py stage-check --index testpypi
54
+ - name: Reject an existing production version
55
+ if: github.event_name == 'release'
56
+ run: python tools/index_release.py unused --index pypi
57
+ testpypi:
58
+ needs: staging-status
59
+ # Explicit opt-in: nothing is uploaded unless the repository enables staging.
60
+ if: needs.staging-status.outputs.already_staged != 'true' && vars.TESTPYPI_RELEASE_ENABLED == 'true'
61
+ runs-on: ubuntu-latest
62
+ environment:
63
+ name: testpypi
64
+ url: https://test.pypi.org/p/diag3d
65
+ permissions:
66
+ id-token: write
67
+ steps:
68
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
69
+ with:
70
+ name: python-dist
71
+ path: dist/
72
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
73
+ with:
74
+ repository-url: https://test.pypi.org/legacy/
75
+ skip-existing: false
76
+ print-hash: true
77
+ attestations: true
78
+ verify-testpypi:
79
+ needs: [staging-status, testpypi]
80
+ if: ${{ !cancelled() && vars.TESTPYPI_RELEASE_ENABLED == 'true' && needs.staging-status.result == 'success' && (needs.testpypi.result == 'success' || needs.testpypi.result == 'skipped') }}
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
84
+ with:
85
+ persist-credentials: false
86
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
87
+ with:
88
+ python-version: '3.12'
89
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
90
+ with:
91
+ name: python-dist
92
+ path: dist/
93
+ - run: python tools/index_release.py verify --index testpypi
94
+ pypi:
95
+ needs: verify-testpypi
96
+ if: ${{ !cancelled() && needs.verify-testpypi.result == 'success' && github.event_name == 'release' && vars.PYPI_RELEASE_ENABLED == 'true' }}
97
+ runs-on: ubuntu-latest
98
+ environment:
99
+ name: pypi
100
+ url: https://pypi.org/p/diag3d
101
+ permissions:
102
+ id-token: write
103
+ steps:
104
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
105
+ with:
106
+ name: python-dist
107
+ path: dist/
108
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
109
+ with:
110
+ skip-existing: false
111
+ print-hash: true
112
+ attestations: true
113
+ verify-production:
114
+ needs: pypi
115
+ runs-on: ubuntu-latest
116
+ steps:
117
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
118
+ with:
119
+ persist-credentials: false
120
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
121
+ with:
122
+ python-version: '3.12'
123
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
124
+ with:
125
+ name: python-dist
126
+ path: dist/
127
+ - run: python tools/index_release.py verify --index pypi
128
+ release-assets:
129
+ needs: verify-production
130
+ runs-on: ubuntu-latest
131
+ permissions:
132
+ contents: write
133
+ steps:
134
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
135
+ with:
136
+ name: python-dist
137
+ path: dist/
138
+ - name: Attach the verified distributions and checksums
139
+ env:
140
+ GH_TOKEN: ${{ github.token }}
141
+ GH_REPO: ${{ github.repository }}
142
+ TAG: ${{ github.event.release.tag_name }}
143
+ run: |
144
+ cd dist
145
+ sha256sum *.whl *.tar.gz > SHA256SUMS
146
+ gh release upload "$TAG" *.whl *.tar.gz SHA256SUMS
@@ -0,0 +1,32 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .venv/
7
+ venv/
8
+ build/
9
+ dist/
10
+ wheelhouse/
11
+ .env
12
+ .env.*
13
+ *.pem
14
+ *.key
15
+ *.p12
16
+ *.pfx
17
+ .DS_Store
18
+ gallery/
19
+ /validation/
20
+ benchmark_outputs/
21
+ results/
22
+ .industry_cache/
23
+ examples/industry_data/
24
+ *.log
25
+
26
+ /Diag3D-v0.2-research-evidence.html
27
+
28
+ lifecycle_output/
29
+ .pypirc
30
+ .venv*/
31
+ *.whl
32
+ *.tar.gz
@@ -0,0 +1,70 @@
1
+ # Architecture
2
+
3
+ The authoritative 0.2.0.dev0 development tree is created from the supplied v0.1 implementation. Legacy modules remain; research modules are additive and import the established Plotly/report shell.
4
+
5
+ | Module | Responsibility and principal interfaces |
6
+ |---|---|
7
+ | `research/types.py` | Typed `PredictionRecord`, columnar `PredictionBatch`, `FoldPlan`, `OOFResult`, `ErrorField`, `FailureRegion`, `RegionMetrics`, `PersistenceResult`, `StabilityResult`, `CrossModelRegionAnalysis`, `SupportDiagnostics`, `FailureCharacterization`, `EvidenceScore`, `RepairExperimentResult`, `AnalysisResult` |
8
+ | `research/config.py` | Validated analysis limits, score weights, characterization thresholds |
9
+ | `research/oof.py` | Input validation, immutable reused fold plan, seeded cloned estimators, train-fold transforms, OOF predictions, train-relative support |
10
+ | `research/fields.py` | Uniform/quantile 1D/2D binning, weighted empirical loss aggregation, support mask; `ErrorFieldEstimator` protocol |
11
+ | `research/discovery.py` | Connected components, threshold lineages, core/extent geometry, Jaccard deduplication; `DiscoveryStrategy` protocol |
12
+ | `research/stability.py` | Conditional row/group bootstrap and held-out fold rediscovery |
13
+ | `research/diagnostics.py` | Cross-model disagreement, support interpretation, explicit taxonomy, exposed weighted ranking |
14
+ | `research/analyzer.py` | `FailureAnalyzer.fit_analyze`, default model factories, bounded orchestration, reproducibility metadata |
15
+ | `research/repairs.py` | Nested training-only selection; weighting, interaction, alternative estimator and resampling experiments |
16
+ | `research/explorer.py` | Offline 1D/3D evidence figures, exact exposed cell boundaries, model/overlay controls, report sections and region inspection |
17
+ | `research/benchmarks.py` | Six deterministic mechanisms, planted masks/bounds, localization evaluation |
18
+ | `experiments/` | Reproducible full suite, seed/grid sensitivity and null audit |
19
+ | `cli.py` | Seven commands; new `analyze` delegates to `FailureAnalyzer` |
20
+
21
+ `AnalysisResult` retains numeric fields, OOF batches and typed regional evidence. `.visualize()` returns a Plotly figure; `.report(path)` writes standalone HTML; `.save(directory)` also writes strict JSON and observation-level OOF CSV. JSON export has no pickled estimators or executable payloads.
22
+
23
+ ## Preserved v0.1 architecture
24
+
25
+ - `dataset.py`: strict CSV loading, Polars frames, deterministic row caps and original Dataset API.
26
+ - `profiling.py`, `diagnostics.py`, `relationships.py`: column profiling, diagnostic flags, VIF, dependence and exploratory pair screening.
27
+ - `modeling.py`: original single-held-out baseline result and metrics.
28
+ - `visualization.py`, `surfaces.py`, `spatial.py`: relationship clouds, residual/disagreement landscapes, support-masked response surfaces and coordinate views.
29
+ - `reporting.py`: offline Plotly bundling, responsive report shell, lazy rendering and camera controls.
30
+ - `utils.py`, `config.py`: shared formatting, errors, limits and centralized version.
31
+
32
+ The original 2,713 source lines were inspected. Machine-readable public class/function inventory is in `docs/v01_inventory.json`. The original archive's 79 tests passed before changes. Generated legacy industry galleries and downloaded datasets are excluded from source; their 12 asset checks remain optional and skip when the assets are absent. The other 67 baseline tests remain active.
33
+
34
+ ## Source provenance
35
+
36
+ | Input | Role | SHA-256 |
37
+ |---|---|---|
38
+ | `Diag3D-v0.1.0(1).zip` | Implementation foundation | `d2cb469745008c968cf7a2917e232b6dd15ee298a9a392c238ffe0a5c89ce6a6` |
39
+ | `Diag3D-packaging-candidate.zip` | Reference for dynamic versioning, artifact exclusions, metadata and release separation | `21eb9c15ce47fff942ffc23ff71ba7088229083069ee96e13158d82d212065e2` |
40
+
41
+ No recovered research-reset source was substituted. Packaging reference science modules were not copied over v0.1. Public samples are recreated from generators or fetched with documented attribution; research data, caches, model binaries and generated galleries are not committed.
42
+
43
+ ## Performance and extension boundaries
44
+
45
+ Fold indices, observation-level OOF losses and support transforms are computed once and reused. Uniform/quantile field aggregation uses bincount and bounded grids. Nearest-neighbor queries avoid materializing n×n distance matrices, although high-dimensional nearest-neighbor search can still be expensive. Estimator `n_jobs` is configurable; folds run sequentially to bound memory and BLAS thread pools are capped. Feature count, pair count, grid resolution, candidate count and bootstrap replicates are bounded.
46
+
47
+ Bootstrap currently recalculates grids per candidate/replicate, preserving simple auditable logic at additional cost. It does not cache results to disk. The field/discovery protocols allow compatible grid strategies; custom geometry would also require changing matching, membership and visualization contracts. Categorical discovery, classification, arbitrary-dimensional regions, persistent homology and full model-refit bootstrap are not implemented.
48
+
49
+
50
+ ## Null-validation modules
51
+
52
+ The existing v0.2 tree is extended by `calibration.py`, `nulls.py`, `refit.py`, `nested.py`, and optional `figures.py`. Public methods are `FailureAnalyzer.calibrate`, `refit_bootstrap`, and `validate_repairs`. Null maxima repeat the complete configured search; duplicate bootstrap identities never cross folds; strategy selection accepts outer-training data only. Typed results retain replicate maxima, p calculations, refit geometries and outer-fold predictions. See [detailed architecture](docs/architecture.md).
53
+
54
+
55
+ ## Lifecycle layer (`src/diag3d/lifecycle/`)
56
+
57
+ | Module | Responsibility |
58
+ |---|---|
59
+ | `findings.py` | `Finding` (observation vs hypothesis, rule/version, evidence, counts, threshold, status incl. `not_evaluable`) |
60
+ | `predictions.py` | `PredictionTable` keyed by `(sample_id, model, repeat, fold, seed, role, checkpoint)`; loaders; alignment |
61
+ | `run.py` | `Run` directory: manifest, events, split, predictions, findings, artifacts; atomic writes; lock |
62
+ | `preflight.py` | Data-quality and split-design checks; no model fitting |
63
+ | `monitor.py` | `TrainingMonitor` events, checkpoint checks, LightGBM callback |
64
+ | `investigate.py` | Bounded advisory follow-up checks with audit records |
65
+ | `posthoc.py` | Frozen-prediction diagnostics; `oof_from_predictions` bridge to `FailureAnalyzer.analyze_oof` |
66
+ | `splits.py` | `SplitManifest`: explicit or verified per-fold membership |
67
+ | `report.py`, `figures.py` | Redacted outputs, `export_error_glb` (via the canonical `model_space/glb.py` exporter), optional Matplotlib figures |
68
+ | `workflow.py` | `preflight_run`, `posthoc_run`, `reproduce_posthoc` |
69
+
70
+ `research/oof.assemble_oof` is shared by fitted and imported predictions. See [docs/lifecycle.md](docs/lifecycle.md).
@@ -0,0 +1,29 @@
1
+ # Before / after explorer comparison
2
+
3
+ | Aspect | Existing v0.2 research explorer | New model-space explorer |
4
+ |---|---|---|
5
+ | Entry | Static research report with embedded local-error scenes | Immediate shared 3D environment |
6
+ | Primary identity | Failure-region evidence | Pre-run and post-run spatial model exploration, with research evidence integrated |
7
+ | Navigation | Report sections | DATA → MODEL → PREDICTION → ERROR → FAILURE → REPAIR |
8
+ | Data before training | Separate legacy diagnostic report | Same viewport, missingness/support/target/correlation/shift inspection |
9
+ | Supplied y/y_pred | Residual visualization command | Full CSV model-space workflow, no model file |
10
+ | Camera and selection | Separate scenes and controls | One stateful camera and persistent row/region identity |
11
+ | Failure evidence | Existing model/overlay/region controls | Same evidence in a compact inspector, exact union outlines and explicit source model |
12
+ | Repair | Reported numerical experiments | Actual outer-prediction endpoint replay and guardrail table |
13
+ | Classification | No calibrated discovery engine | Class/probability/confidence/error exploration; inference limitation explicit |
14
+ | Fitted models | Existing research estimator API | Optional adapter for model queries and selected linear/tree explanations |
15
+ | Browser validation | Prior pixel rendering unverified | Chromium/WebGL tested, including mobile sizing and zero external requests |
16
+
17
+ The original evidence report remains separately available. Its only code change is renaming a colliding browser observer variable; numerical evidence and discovery logic are unchanged.
18
+
19
+ ## Existing research scene
20
+
21
+ ![Original research explorer layout](docs/model-space/00-research-explorer-before.png)
22
+
23
+ ## Unified failure space
24
+
25
+ ![New failure space](docs/model-space/05-failure.png)
26
+
27
+ ## Validated repair endpoint
28
+
29
+ ![New repair space](docs/model-space/07-repair-after.png)
@@ -0,0 +1,72 @@
1
+ # Actual synthetic benchmark results
2
+
3
+ Executed locally with version 0.2.0.dev0, seed 42, 2,400 observations per case, five validation folds, 20 conditional bootstrap replicates, ten grid bins, minimum cell support 4 and region support 20. All values below come from actual runs. No ground-truth masks are supplied to the detector.
4
+
5
+ ## Localization and stability
6
+
7
+ | Case | Best IoU | Precision | Recall | Samples | Error lift | Persistence | Bootstrap | Eligible-fold recurrence | Diagnostic candidate |
8
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---|
9
+ | A | 0.9955 | 1.0000 | 0.9955 | 220 | 4.490× | 100% | 100% | 100% (5/5 eligible) | SHARED_STRUCTURAL_FAILURE |
10
+ | B | 1.0000 | 1.0000 | 1.0000 | 68 | 17.556× | 100% | 100% | 100% (5/5 eligible) | DATA_SPARSE_FAILURE |
11
+ | C | 1.0000 | 1.0000 | 1.0000 | 384 | 5.099× | 100% | 100% | 100% (5/5 eligible) | SHARED_STRUCTURAL_FAILURE |
12
+ | D | 1.0000 | 1.0000 | 1.0000 | 449 | 3.778× | 100% | 65% | 100% (1/5 eligible) | EXTRAPOLATION_FAILURE |
13
+ | F | 0.9955 | 1.0000 | 0.9955 | 220 | 4.487× | 100% | 100% | 100% (5/5 eligible) | MODEL_SPECIFIC_FAILURE |
14
+
15
+ IoU is observation-membership Jaccard. Benchmark scoring chooses the best-IoU candidate; the detector never sees ground truth. In D, the broader extent is F-02; the top-ranked severe core covers approximately half the true region. The 1/1 eligible-fold result in D must not be described as recurrence across all five folds.
16
+
17
+ ## Primary acceptance: A
18
+
19
+ - Planted rectangle: 0.2 < x1 < 0.8 and 0.2 < x2 < 0.8; 221 true observations.
20
+ - Detected F-01: exact union of nine adjacent cells, with enclosing bounds x1 [0.200173, 0.799691], x2 [0.199706, 0.799040]; 220 observations.
21
+ - Source-model MAE 3.8363, global MAE 0.8543, lift 4.4904×; local relative density 1.0652 and training-neighbor distance ratio 0.9629.
22
+ - Threshold lineage spans all six thresholds from 1.25× to 4×, with 20/20 bootstrap detections and 5/5 eligible fold detections.
23
+ - Shared-error candidate among two deliberately restricted Ridge baselines. These are two estimators from one family, not two independent model families.
24
+ - Report includes the computed surface, support/disagreement/persistence overlays, exact cell edges and region inspection. A static figure is generated by `experiments/plot_acceptance.py`.
25
+
26
+ ## Distinct secondary signatures
27
+
28
+ - B: relative density 0.0634, training-neighbor distance ratio 3.476; sparse support is the primary category.
29
+ - D: range-exceedance fraction 100%, training-neighbor distance ratio 4.012; extrapolation is primary.
30
+ - F: regional Ridge MAE 4.5995 versus HGB 0.1995; affected model is Ridge.
31
+ - C: high irreducible noise is detected as shared dense error. The taxonomy cannot causally distinguish noise from structural misspecification; this is a known limitation.
32
+
33
+ ## Grouped leakage: E
34
+
35
+ | Validation | MAE | RMSE | R² |
36
+ |---|---:|---:|---:|
37
+ | Random KFold | 0.110541 | 0.138239 | 0.999261 |
38
+ | GroupKFold | 6.044717 | 7.660880 | -1.269610 |
39
+
40
+ Group-aware MAE is 54.68 times random-CV MAE. There is no unique planted geometric region in E, so localization precision/recall/IoU are not reported.
41
+
42
+ ## Nested repair experiments on A
43
+
44
+ Geometry is rediscovered in each outer training fold through inner OOF prediction. Outer test outcomes do not select that geometry. Metrics pool fold-specific regimes. The second Ridge estimator is the predeclared alternative in this benchmark.
45
+
46
+ | Strategy | Global MAE before → after | Regional MAE before → after | Outside MAE before → after | Success criteria met |
47
+ |---|---:|---:|---:|---|
48
+ | targeted_weighting | 0.8557 → 1.3664 | 3.8321 → 2.2773 | 0.5553 → 1.2745 | False |
49
+ | interaction_augmentation | 0.8557 → 0.0994 | 3.8321 → 0.0928 | 0.5553 → 0.1001 | True |
50
+ | alternative_estimator | 0.8557 → 0.8543 | 3.8321 → 3.8363 | 0.5553 → 0.5534 | False |
51
+ | support_rebalancing | 0.8557 → 1.3659 | 3.8321 → 2.2793 | 0.5553 → 1.2737 | False |
52
+
53
+ Weighting and rebalancing improve the selected regime while degrading the rest of the model, and are therefore rejected. Interaction augmentation improves all three metrics. This validates this synthetic procedure only, not general remediation effectiveness.
54
+
55
+ ## Failed attempts and remaining weaknesses
56
+
57
+ - The first implementation chose only the severity-support core: D IoU was 0.5011. A broader excess-error representative improved D to 1.0 but degraded A and F to 0.4944. The final method retains both materially distinct representatives; core and extent remain correlated candidates. This development used the same benchmark suite, so it is not an independent methodological comparison.
58
+ - F has 4 candidates with IoU <0.1 to the planted mask, out of 11 candidates. These include boundary effects and spurious candidates; they are not discarded to improve the report.
59
+ - A grid/seed sensitivity check (seeds 17,43,101; grids 8,10,13; 1,600 rows) gives best-IoU range 0.704–1.000. Axis alignment matters.
60
+ - Null-data audit uses five homoscedastic linear datasets (no planted localized failure). Results below demonstrate that recurrence does not establish significance.
61
+
62
+ | Null seed | Candidate count | Highest priority score | Candidates with bootstrap and fold recurrence ≥0.6 |
63
+ |---|---:|---:|---:|
64
+ | 0 | 1 | 47.76 | 1 |
65
+ | 1 | 1 | 21.83 | 0 |
66
+ | 2 | 4 | 45.42 | 2 |
67
+ | 3 | 1 | 19.69 | 0 |
68
+ | 4 | 2 | 32.21 | 0 |
69
+
70
+ **Next research priority:** independent discovery/confirmation evaluation with null-calibrated thresholds and explicit false-discovery/power assessment.
71
+
72
+ Raw measured summaries: `docs/validation/benchmark_results.json` and `docs/validation/sensitivity_results.json`. Regenerate full reports and prediction records with the experiments runner.
@@ -0,0 +1,98 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — first public release (2026-09-24)
4
+
5
+ First release on PyPI as `diag3d` (`pip install diag3d`).
6
+
7
+ Fixed: the training monitor recorded prediction part paths as OS-native strings and
8
+ consolidated them by looking for a forward-slash prefix, so on Windows no part was ever
9
+ registered and analysing a run failed with `KeyError: 'final'`. Parts are now recorded
10
+ as POSIX paths and matched by file name; a regression test and a Windows CI job cover it.
11
+ The killed-writer recovery test now uses a portable process kill.
12
+
13
+ Fixed on Windows: run manifests, findings and explorer assets are read as UTF-8 regardless
14
+ of the platform code page (they were always written as UTF-8), research outputs are written
15
+ as UTF-8, and the `diag3d` CLI and the lifecycle examples no longer crash when the console or
16
+ a redirected pipe cannot encode a character such as `→` or `ρ`: the echo degrades to `?`
17
+ while every file stays UTF-8. The Windows CI job runs under the default code page.
18
+
19
+ Added `diag3d.baselines`: reference baselines (`linear`, `rf`, `hgb`, and `xgb`, `lgbm`,
20
+ `mlp` when their optional packages are installed) fitted on every partition of one
21
+ explicit split manifest with shared preprocessing, documented default parameters,
22
+ deterministic seeds, fit/predict wall-clock timings and environment metadata, returning
23
+ a validated `PredictionTable`. Added `diag3d.lifecycle.partitions` (training rows versus
24
+ evaluation rows of a manifest on a caller's row order) and widened the documented public
25
+ surface of `diag3d.lifecycle` (`docs/public_api.md`).
26
+
27
+ Release engineering: a "Prepare release" workflow creates the version tag and a draft
28
+ GitHub Release with notes from this changelog; the release workflow stages on TestPyPI
29
+ and publishes to PyPI through trusted publishing with attestations, verifies the
30
+ published files and attaches them to the release; the build backend is pinned exactly so
31
+ a rebuild of the tagged commit reproduces the validated bytes; a Homebrew tap
32
+ (`falhezaimi/homebrew-tap`) is maintained separately.
33
+
34
+ ## Unreleased — consolidation into main
35
+
36
+ Merged the model-space explorer (PR #1), GLB export (PR #4) and release readiness
37
+ (PR #2), with safeguards ported from PR #3. One GLB exporter (the explorer's) replaces
38
+ the lifecycle point-cloud exporter, which mirrored scenes and mis-encoded colours.
39
+ The training monitor records observed fold membership and stores it as the run's split
40
+ manifest. Run schema 2.1 records conventions for nested CV (outer loop only), multiple
41
+ evaluation datasets (one linked dataset per table) and checkpoint units (declared).
42
+
43
+ ## Unreleased — lifecycle foundation audit
44
+
45
+ Training membership is no longer inferred from fold labels unless a complement design
46
+ (K-fold, repeated/group K-fold, leave-one-group-out) is declared and verified; other
47
+ designs need an explicit `SplitManifest`, otherwise dependent checks are `not_evaluable`.
48
+ Run schema 2.0 (split manifest, part-based prediction tables with dataset links, model
49
+ registry, per-diagnostic Diag3D version, writer ids on events; 1.x readable read-only).
50
+ Fixed: future/buffer rows counted as training data; complement taken across repeats;
51
+ missing inputs reported as `pass` (small subgroups, single-role splits, missing times);
52
+ test-set samples accepted under non-test roles during training; investigator reference
53
+ defaulting to all samples; row-order-dependent results; int64 fingerprint collisions;
54
+ duplicate CSV headers accepted; ECE bin edges off at exact boundaries; `status()` ignoring
55
+ session order and Ctrl-C; resumed writers corrupting a torn event line and overwriting a
56
+ crashed session's prediction parts; ~1 GB memory spike when finishing long runs.
57
+ Added `r2_mean_over_repeats`, `tools/lifecycle_mutations.py` (20/20 mutations caught), 44 adversarial tests.
58
+
59
+ ## Unreleased — lifecycle diagnostics (version string remains 0.2.0.dev0)
60
+
61
+ Added `diag3d.lifecycle`: shared `Finding` records; validated long-format `PredictionTable`
62
+ with composite keys, explicit column mapping and CSV/Parquet/NPZ/structured-NPY loading
63
+ (pickle/joblib only with `trusted=True`); directory `Run` store with versioned manifest,
64
+ append-only events, atomic writes, fingerprints and a single-writer lock; `preflight`;
65
+ `TrainingMonitor` with a LightGBM callback, test-label guard and bounded memory; bounded
66
+ `Investigator`; `analyze_predictions` for frozen predictions; redacted Markdown/JSON/CSV
67
+ outputs; `.glb` export with a companion manifest; optional Matplotlib figures. New CLI
68
+ commands `preflight`, `posthoc`, `run-show`. `FailureAnalyzer.analyze_oof` runs region
69
+ discovery on imported OOF predictions without fitting.
70
+
71
+ Fixed: `python -m diag3d.cli analyze` failed with "No such command" (the `__main__` guard
72
+ preceded the command). `r2_of_mean_prediction` is now `None` for a constant target instead
73
+ of scikit-learn's 1.0/0.0.
74
+
75
+ ## Unreleased — release preparation
76
+
77
+ - Preserve the unapproved `0.2.0.dev0` research version and single version source.
78
+ - Validate both distributions, clean installations and installed OOF analysis.
79
+ - Add stable-tag OIDC release automation with exact TestPyPI artifact verification,
80
+ duplicate-version rejection and an explicit production enable gate.
81
+ - Extend source/history inspection and document Trusted Publishing, dependency
82
+ groups and the complete maintainer release procedure.
83
+
84
+ ## 0.2.0.dev0
85
+
86
+ Implemented the research reset from v0.1: typed OOF failure-region engine, supported empirical error grids, threshold persistence, bootstrap/fold recurrence, cross-model/support diagnostics, transparent characterization and ranking, nested repair experiments, synthetic benchmarks, evidence explorer, research CLI/report, and development packaging. Preserved the legacy CSV API/commands and report infrastructure. Production publication remains disabled.
87
+
88
+ ## 0.2.0.dev0 — null-validation development snapshot
89
+
90
+ Adds search-wide empirical calibration, six negative controls, model-refit bootstrap, training-only nested strategy selection, explicit repair guardrails, statistical explorer overlays, publication figures and measured false-positive/detection experiments. Retains the prior implementation and its regression coverage. Production publication remains disabled.
91
+
92
+ ## Model-space development snapshot — 2026-09-21
93
+
94
+ - Added a unified Data/Model/Prediction/Error/Failure/Repair explorer and offline session contract.
95
+ - Added CSV supplied-output reports, pre-run diagnostics, optional fitted-model adapters and evidence-backed repair replay.
96
+ - Preserved the existing numerical research engine and original reporting APIs.
97
+ - Fixed a duplicate observer declaration in the research HTML.
98
+ - Added three redistributable demos, browser screenshots, source audit, architecture/UX specifications and 25 Python tests.
@@ -0,0 +1,5 @@
1
+ # Contributing
2
+
3
+ Install `python -m pip install -e '.[dev]'`. Run `python -m pytest -q`, `python -m ruff check src tests experiments tools`, `python -m compileall -q src`, and the synthetic benchmark runner. Add tests for scientific invariants, leakage boundaries, stability and meaningful failure modes. Preserve failed benchmarks. Explain changes to estimands, fold design, overlap definitions or ranking formulas in `docs/research_method.md`.
4
+
5
+ Do not commit datasets, generated reports, model artifacts, credentials, caches or environments. Production publishing and stable tags are disabled during the research reset. Discuss methodological claims with evidence and relevant prior art; do not infer causation from regional error alone.
@@ -0,0 +1,18 @@
1
+ # Flagship walkthrough — 28 seconds
2
+
3
+ Open `gallery/model-space/regression.html` in a WebGL-capable desktop browser. Press **28s walkthrough**. A 1600×1000 recording provides readable axes and evidence; 1920×1080 is also suitable. The controls keep the same camera and selection as the layers change.
4
+
5
+ | Time | Visual | Suggested narration |
6
+ |---|---|---|
7
+ | 0–4 s | Raw benchmark points in feature space | “Start with an ordinary machine-learning results table.” |
8
+ | 4–8 s | Prediction field and residual lines appear | “Enter the relationship between observations and predictions.” |
9
+ | 8–12 s | Prediction geometry | “See the response your model produces where data provide support.” |
10
+ | 12–16 s | Error height and color | “Turn error into a spatial structure.” |
11
+ | 16–18 s | Exact failure outlines | “Locate a recurring failure region.” |
12
+ | 18–22 s | Region selected; calibration/refit inspector | “Inspect its support, recurrence and statistical assumptions.” |
13
+ | 22–25.5 s | Original outer-test predictions | “A repair is chosen using training-side validation.” |
14
+ | 25.5–28 s | Selected repair predictions and smaller residuals | “Then inspect the change on untouched outer-test observations.” |
15
+
16
+ The exported MP4 is assembled from actual rendered browser captures of these states, with discrete cuts; it is not a continuous screen recording. The HTML button runs the interactive sequence for a fresh recording. No interpolated intermediate predictions are represented as a trained model.
17
+
18
+ For a public caption, retain “synthetic interaction benchmark” and “research development version.” Do not generalize its 220/221 localization, 50/50 refit recurrence or large repair gain to real datasets. Keep the unresolved null-calibration caveat available in the linked evidence.