clusteraudiencekit 7.3.1__tar.gz → 7.4.2__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.
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.github/workflows/ci.yml +4 -4
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.github/workflows/tests.yml +2 -2
- clusteraudiencekit-7.4.2/.github/workflows/wheels.yml +86 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.gitignore +2 -0
- clusteraudiencekit-7.4.2/.graphifyignore +31 -0
- clusteraudiencekit-7.4.2/AUDIT.md +64 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/CHANGELOG.md +82 -1
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/Cargo.lock +265 -231
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/Cargo.toml +7 -8
- clusteraudiencekit-7.4.2/PKG-INFO +282 -0
- clusteraudiencekit-7.4.2/README.md +232 -0
- clusteraudiencekit-7.4.2/TECHNICAL_DEBT.md +96 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/benches/benchmarks.rs +2 -1
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/ROADMAP_HONEST.md +221 -4
- clusteraudiencekit-7.4.2/docs/bench/kmeans_1m_results.md +48 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/pyproject.toml +8 -2
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/algorithms.rs +3 -3
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/b2b_governance.rs +3 -3
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/clustering.rs +15 -15
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/clv.rs +46 -1
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/drift_detection.rs +68 -1
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/lookalike.rs +85 -2
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/mod.rs +1 -1
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/neural_networks.rs +17 -21
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/pattern_discovery.rs +2 -2
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/price_intelligence.rs +6 -6
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/privacy.rs +86 -6
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/profiling.rs +192 -7
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/quality_metrics.rs +59 -20
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/revenue_intelligence.rs +8 -8
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/segment_intelligence.rs +6 -6
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/sql_export.rs +8 -10
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/streaming.rs +2 -2
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/temporal_analytics.rs +5 -5
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/lib.rs +2 -5
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/python.rs +38 -14
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/utils/conversions.rs +3 -3
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/utils/validation.rs +2 -2
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/tests/test_wired_modules.py +25 -0
- clusteraudiencekit-7.4.2/tools/graphify/README.md +63 -0
- clusteraudiencekit-7.4.2/tools/graphify/graphify_adapter.py +242 -0
- clusteraudiencekit-7.3.1/PKG-INFO +0 -256
- clusteraudiencekit-7.3.1/README.md +0 -207
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.env.example +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.gitattributes +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.github/dependabot.yml +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.github/pull_request_template.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/.pre-commit-config.yaml +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/CONTRIBUTING.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/LICENSE +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/Makefile +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/SECURITY.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/clusteraudiencekit/__init__.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/CODE_OF_CONDUCT.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/CONTRIBUTORS.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/SECURITY_AUDIT.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/SQL_EXPORT.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/architecture/README.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/ARCHITECTURE.md.fabricated-ecosystem +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/CI_ERRORS.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/CONTRIBUTING.md.claude-dupe +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/PYPI_UPLOAD.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/README.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/ROADMAP.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/SECURITY.md.stale-duplicate +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/WORKFLOW_INTEGRATION.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/api-reference.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/architecture.md.stale +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/comparison.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/error-catalog.json +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/getting-started-simple.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/performance-comparison.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/press-release.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/docs/archive/troubleshooting.md +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/examples/basic_segmentation.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/examples/sql_export_example.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/examples/streaming_updates.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/install.sh +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/rust-toolchain.toml +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/scripts/install-curl.sh +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/scripts/install-from-source.sh +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/scripts/install-pip.ps1 +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/scripts/install-pip.sh +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/scripts/test_dashboards_crossplatform.sh +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/activation.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/activation_orchestrator.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/b2b_segmentation.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/behavioral.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/churn_prediction.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/cohorts.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/dashboard.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/governance.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/heuristic_score_estimator.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/k_estimation.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/lifecycle.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/platform_adapters.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/plugins.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/rfm.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/engine/segments.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/src/utils/mod.rs +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/tests/test_basic.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/tests/test_clustering.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/tests/test_performance.py +0 -0
- {clusteraudiencekit-7.3.1 → clusteraudiencekit-7.4.2}/tests/test_sql_export.py +0 -0
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
name: Rust Build & Test
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v7
|
|
15
15
|
|
|
16
16
|
- uses: dtolnay/rust-toolchain@stable
|
|
17
17
|
|
|
@@ -36,9 +36,9 @@ jobs:
|
|
|
36
36
|
matrix:
|
|
37
37
|
python-version: ["3.10", "3.11", "3.12"]
|
|
38
38
|
steps:
|
|
39
|
-
- uses: actions/checkout@
|
|
39
|
+
- uses: actions/checkout@v7
|
|
40
40
|
|
|
41
|
-
- uses: actions/setup-python@
|
|
41
|
+
- uses: actions/setup-python@v7
|
|
42
42
|
with:
|
|
43
43
|
python-version: ${{ matrix.python-version }}
|
|
44
44
|
|
|
@@ -63,7 +63,7 @@ jobs:
|
|
|
63
63
|
name: Cargo Security Audit
|
|
64
64
|
runs-on: ubuntu-latest
|
|
65
65
|
steps:
|
|
66
|
-
- uses: actions/checkout@
|
|
66
|
+
- uses: actions/checkout@v7
|
|
67
67
|
|
|
68
68
|
- uses: dtolnay/rust-toolchain@stable
|
|
69
69
|
|
|
@@ -7,8 +7,8 @@ jobs:
|
|
|
7
7
|
matrix:
|
|
8
8
|
python-version: ["3.10", "3.11", "3.12"]
|
|
9
9
|
steps:
|
|
10
|
-
- uses: actions/checkout@
|
|
11
|
-
- uses: actions/setup-python@
|
|
10
|
+
- uses: actions/checkout@v7
|
|
11
|
+
- uses: actions/setup-python@v7
|
|
12
12
|
with:
|
|
13
13
|
python-version: ${{ matrix.python-version }}
|
|
14
14
|
- name: Install
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: Build & Test Wheels
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ['v*']
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build-wheels:
|
|
12
|
+
name: Build wheels on ${{ matrix.os }}
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
include:
|
|
17
|
+
- os: ubuntu-latest
|
|
18
|
+
target: x86_64
|
|
19
|
+
- os: macos-14
|
|
20
|
+
target: aarch64-apple-darwin
|
|
21
|
+
- os: macos-14
|
|
22
|
+
target: x86_64-apple-darwin
|
|
23
|
+
- os: windows-latest
|
|
24
|
+
target: x86_64
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v7
|
|
28
|
+
|
|
29
|
+
- name: Set up Python
|
|
30
|
+
uses: actions/setup-python@v7
|
|
31
|
+
with:
|
|
32
|
+
python-version: '3.11'
|
|
33
|
+
|
|
34
|
+
- name: Set up Rust
|
|
35
|
+
if: runner.os != 'macOS'
|
|
36
|
+
uses: dtolnay/rust-toolchain@stable
|
|
37
|
+
|
|
38
|
+
- name: Build wheels (macOS, via maturin-action for cross-compilation)
|
|
39
|
+
if: runner.os == 'macOS'
|
|
40
|
+
uses: PyO3/maturin-action@v1
|
|
41
|
+
with:
|
|
42
|
+
target: ${{ matrix.target }}
|
|
43
|
+
args: --release --out dist
|
|
44
|
+
|
|
45
|
+
- name: Build wheels (Linux/Windows, native)
|
|
46
|
+
if: runner.os != 'macOS'
|
|
47
|
+
run: |
|
|
48
|
+
pip install --upgrade pip
|
|
49
|
+
pip install maturin
|
|
50
|
+
maturin build --release --out dist
|
|
51
|
+
|
|
52
|
+
- name: Test wheel
|
|
53
|
+
# Skip on the cross-compiled Intel-from-ARM leg -- the wheel is
|
|
54
|
+
# built for a different CPU architecture than this runner.
|
|
55
|
+
if: matrix.target != 'x86_64-apple-darwin'
|
|
56
|
+
shell: bash
|
|
57
|
+
run: |
|
|
58
|
+
pip install dist/*.whl
|
|
59
|
+
pip install pytest scikit-learn
|
|
60
|
+
pytest tests/ -q
|
|
61
|
+
|
|
62
|
+
- name: Upload wheel artifact
|
|
63
|
+
uses: actions/upload-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: wheels-${{ matrix.os }}-${{ matrix.target }}
|
|
66
|
+
path: dist/*.whl
|
|
67
|
+
|
|
68
|
+
publish:
|
|
69
|
+
name: Publish to PyPI
|
|
70
|
+
needs: build-wheels
|
|
71
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
environment: pypi
|
|
74
|
+
permissions:
|
|
75
|
+
id-token: write
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/download-artifact@v4
|
|
78
|
+
with:
|
|
79
|
+
path: dist
|
|
80
|
+
pattern: wheels-*
|
|
81
|
+
merge-multiple: true
|
|
82
|
+
|
|
83
|
+
- name: Publish to PyPI
|
|
84
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
85
|
+
with:
|
|
86
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Graphify ignore file - https://docs.graphify.com/reference/configuration
|
|
2
|
+
# Keep the scan (and therefore the agent's graph) focused on real source.
|
|
3
|
+
.git/
|
|
4
|
+
.github/
|
|
5
|
+
graphify-out/
|
|
6
|
+
|
|
7
|
+
# Build output / artifacts
|
|
8
|
+
target/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
node_modules/
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Caches
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.pyc
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.ruff_cache/
|
|
23
|
+
.tox/
|
|
24
|
+
.nox/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
|
|
28
|
+
# Vendored / generated
|
|
29
|
+
*.min.js
|
|
30
|
+
*.min.css
|
|
31
|
+
*.lock
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Repository Audit — ClusterAudienceKit
|
|
2
|
+
|
|
3
|
+
## Health
|
|
4
|
+
|
|
5
|
+
**GREEN.** This repo arrived already in unusually good shape — three prior
|
|
6
|
+
dedicated passes (OSS-standardization, quick-fix, and a "deep code-review"
|
|
7
|
+
pass that found a real zero-actual-privacy differential-privacy bug) had
|
|
8
|
+
already been merged to `origin/main`, 9 commits ahead of this audit's
|
|
9
|
+
starting clone. This pass fast-forwarded onto that work first, then closed
|
|
10
|
+
a real security gap (3 CVEs via a forced pyo3 major-version bump) and
|
|
11
|
+
verified the external-facing marketing claims against the codebase's own
|
|
12
|
+
source of truth.
|
|
13
|
+
|
|
14
|
+
## Before Audit (this pass's baseline, after fast-forward to `51cffd2`)
|
|
15
|
+
|
|
16
|
+
Open debt: 9
|
|
17
|
+
Critical: 0 · High: 1 · Medium: 7 (6 immediately fixable, 1 deep-transitive) · Low: 2
|
|
18
|
+
|
|
19
|
+
## After Audit
|
|
20
|
+
|
|
21
|
+
Open debt: 3
|
|
22
|
+
Critical: 0 · High: 0 · Medium: 2 (TD-0006 transitive deps, TD-0007 external description) · Low: 1 (TD-0008 benchmark marker)
|
|
23
|
+
|
|
24
|
+
## Items Fixed
|
|
25
|
+
|
|
26
|
+
1. **TD-0001** — 3 real `cargo-audit` CVEs closed: pyo3 buffer-overflow (RUSTSEC-2025-0020), pyo3 missing-`Sync`-bound (RUSTSEC-2026-0177), crossbeam-epoch invalid-pointer-deref (RUSTSEC-2026-0204). Required bumping pyo3 0.22→0.29 and numpy 0.22→0.29.
|
|
27
|
+
2. **TD-0002** — 2 real compile breaks from the pyo3 bump (`PyModule::new_bound`→`new`, `pyo3::PyObject`→`pyo3::Py<PyAny>`).
|
|
28
|
+
3. **TD-0003** — 7 pyo3 0.29 `FromPyObject`-on-`Clone` deprecation warnings, resolved individually per-type (not a blanket fix) based on verified actual usage: 6 types opted into `#[pyclass(from_py_object)]` because they're genuinely extracted from Python arguments; 1 type (`PyFeatureDrift`, construct/return-only) opted out with `#[pyclass(skip_from_py_object)]`.
|
|
29
|
+
4. **TD-0004** — Fixed a real naming bug in the crate's own public API: `ClusterClusterAudienceKitError` → `ClusterAudienceKitError`, mechanically across 18 files and 60+ call sites.
|
|
30
|
+
5. **TD-0005** — Removed a dead `bincode` dependency (zero actual call sites anywhere in the repo) and its dead error-enum variant, closing an "unmaintained crate" `cargo-audit` warning as a side effect.
|
|
31
|
+
|
|
32
|
+
## Items Remaining
|
|
33
|
+
|
|
34
|
+
- **TD-0006** (P2) — 2 residual `cargo-audit` *warnings* (not vulnerabilities): `paste` (unmaintained, transitive via `parquet`) and `anyhow` (unsound, transitive via a non-default-target wasm-tooling chain). Neither is a direct dependency; no safe one-line fix exists.
|
|
35
|
+
- **TD-0007** (P2) — The GitHub repo's short description overstates clustering-algorithm count ("6 clustering algorithms" vs. the project's own `info.algorithms` list of `["kmeans", "kprototypes"]`) and makes an unbenchmarked "1M+ customers in <1s" claim. Documented with exact evidence for the subsequent README/positioning pass; not corrected in this pass per the audit's own sequencing rule (fix the software first, document reality after).
|
|
36
|
+
- **TD-0008** (P3) — Unregistered `pytest.mark.benchmark` produces a harmless warning on every test run.
|
|
37
|
+
|
|
38
|
+
## Future Phase Work
|
|
39
|
+
|
|
40
|
+
Already comprehensively tracked in the repo's own `docs/ROADMAP_HONEST.md` (5 large Rust-only modules awaiting Python bindings, full K-Prototypes categorical support, gradient-boosting integration) — not duplicated here.
|
|
41
|
+
|
|
42
|
+
## CI Status
|
|
43
|
+
|
|
44
|
+
Both workflows (`ci.yml`, `tests.yml`) pass `actionlint` clean. `ruff check .` re-verified clean and genuinely enforced (a prior pass fixed it from silently discarding findings).
|
|
45
|
+
|
|
46
|
+
## Test Status
|
|
47
|
+
|
|
48
|
+
Rust: 439/439 passing (re-verified at every step of the pyo3 bump — before, immediately after the compile fixes, and after the final version bump). Python: 227 passed, 2 skipped, re-verified against a freshly `maturin develop`-built extension after all Rust changes, confirming zero behavioral drift from the pyo3/numpy major-version bump.
|
|
49
|
+
|
|
50
|
+
## Build Status
|
|
51
|
+
|
|
52
|
+
`cargo build --release`, `cargo test --release --no-default-features --lib`, `cargo clippy --workspace --all-targets` (30 warnings, all pre-existing and confirmed unreachable from the Python API — zero new warnings introduced), `cargo fmt --all -- --check`, and `maturin develop --release` all verified clean at the final commit.
|
|
53
|
+
|
|
54
|
+
## Security Status
|
|
55
|
+
|
|
56
|
+
`cargo audit`: 0 vulnerabilities (down from 3), 2 unmaintained/unsound warnings remaining on transitive-only dependencies (documented, not force-fixable).
|
|
57
|
+
|
|
58
|
+
## Dependency Status
|
|
59
|
+
|
|
60
|
+
pyo3/numpy bumped to current major versions (0.29) with zero downstream behavioral breakage beyond the 2 expected compile-time API changes. `crossbeam-epoch` patched via `cargo update --precise`. `bincode` removed entirely (was dead weight).
|
|
61
|
+
|
|
62
|
+
## Final Assessment
|
|
63
|
+
|
|
64
|
+
This repo's own prior audit passes had already done most of the honesty and bug-fixing work; this pass's genuine contribution is closing a real security gap that required a disruptive major-version dependency bump (not a quick pin) across pyo3/numpy, verified with zero behavioral regression across both the Rust and Python test suites, plus fixing two smaller but real bugs (a dead dependency, a public-API naming typo) this project's own prior passes hadn't caught. The one new finding worth flagging loudly: the GitHub repo's external-facing description makes claims ("6 clustering algorithms", "1M+ customers in <1s") that the project's own internal honesty documentation doesn't support — evidence is captured in `TECHNICAL_DEBT.md` TD-0007 for whoever runs the README/positioning pass next. Version bumped to 7.3.2 for this release; PyPI publish intentionally left to the coordinator to handle directly.
|
|
@@ -2,7 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to ClusterAudienceKit are documented here.
|
|
4
4
|
|
|
5
|
-
## [
|
|
5
|
+
## [7.4.2] - 2026-10-17
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Audit remediation: security hardening, correctness fixes, and lint/CI gates.
|
|
9
|
+
|
|
10
|
+
## [7.4.1] - 2026-10-12
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
- **SQL-injection vector through `PyCondition` field names**: the Python
|
|
14
|
+
`PyCondition(field, operator, value)` constructor interpolated `field`
|
|
15
|
+
verbatim into the SQL produced by `Condition::to_sql` (reached via
|
|
16
|
+
`PyBehavioralSegment::export_sql`), with no validation anywhere on that
|
|
17
|
+
path, so e.g. `PyCondition("x; DROP TABLE t; --", ">", 1.0)` emitted the
|
|
18
|
+
payload straight into generated SQL. `field` is now passed through the
|
|
19
|
+
existing `validate_identifier` guard (the same check already enforced in
|
|
20
|
+
`sql_export.rs`), raising `ValueError` for anything that is not a plain
|
|
21
|
+
identifier. New regression test `test_condition_field_rejects_sql_injection`.
|
|
22
|
+
- **`engine::privacy::DifferentialPrivacy::laplace_noise`/`gaussian_noise`
|
|
23
|
+
did not sample any actual randomness**, so `add_laplace_noise`/
|
|
24
|
+
`add_gaussian_noise` (the Python-exposed differential-privacy API)
|
|
25
|
+
provided **zero real privacy protection** despite being documented as a
|
|
26
|
+
real, shipped feature. `laplace_noise` hardcoded `u = 0.5` in place of a
|
|
27
|
+
uniform(0,1) draw, which — because 0.5 sits exactly on the function's
|
|
28
|
+
branch boundary — made the noise term evaluate to exactly `0.0` on every
|
|
29
|
+
call (data returned completely unmodified). `gaussian_noise` hardcoded
|
|
30
|
+
`sigma * 0.5`, a fixed deterministic shift with no randomness. Both now
|
|
31
|
+
draw real randomness via `rand::thread_rng()` (inverse-CDF sampling for
|
|
32
|
+
Laplace; Box-Muller transform for Gaussian). Verified via the real PyO3
|
|
33
|
+
binding: repeated calls with identical arguments now produce different
|
|
34
|
+
output instead of byte-identical/constant output. New regression tests
|
|
35
|
+
`test_laplace_noise_is_actually_random`/
|
|
36
|
+
`test_gaussian_noise_is_actually_random` assert nonzero variance across
|
|
37
|
+
repeated calls; both fail against the pre-fix code. See
|
|
38
|
+
`docs/ROADMAP_HONEST.md`'s "Deep code-review pass (2026-09-27)" for full
|
|
39
|
+
detail.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- `engine::lookalike::LookalikeGenerator::generate_lookalike`'s percentile
|
|
43
|
+
threshold was computed from an unsorted copy of the similarity scores
|
|
44
|
+
(`let sorted: Vec<f64> = similarities.to_vec();` — no `.sort()` call
|
|
45
|
+
despite the name), so the "top N%" cutoff silently depended on the
|
|
46
|
+
candidate list's input order rather than the actual similarity
|
|
47
|
+
distribution. The same 100 candidates fed in a different order produced
|
|
48
|
+
10 vs. 91 lookalikes for an identical "top 10%" request. Fixed by
|
|
49
|
+
actually sorting descending before indexing. New regression test
|
|
50
|
+
`test_percentile_filtering_is_order_independent` (fails against the
|
|
51
|
+
pre-fix code with `left: 10, right: 91`).
|
|
52
|
+
- `engine::drift_detection::DriftDetector::detect_feature_drift`'s
|
|
53
|
+
`DriftMethod::ChiSquare` arm was a hardcoded `0.0` (comment claimed "Use
|
|
54
|
+
KS as proxy" but never called anything), so any caller of
|
|
55
|
+
`detect_feature_drift(..., method="chi_square")` — a real,
|
|
56
|
+
user-selectable, documented option (`parse_drift_method` in
|
|
57
|
+
`src/python.rs` accepts `"chi_square"`/`"chi2"`) — silently got
|
|
58
|
+
`drift_score: 0.0`/`severity: "none"` regardless of actual drift: a total
|
|
59
|
+
false negative. Verified via the real PyO3 binding: a 100x-shifted
|
|
60
|
+
distribution scored 1.0/critical under `"ks"` but 0.0/none under
|
|
61
|
+
`"chi_square"` before the fix. Fixed by actually proxying to
|
|
62
|
+
`kolmogorov_smirnov` (matching the pre-existing `KullbackLeibler` ->
|
|
63
|
+
`hellinger_distance` proxy pattern one line above) — same score/severity
|
|
64
|
+
as KS now. New regression test
|
|
65
|
+
`test_chi_square_drift_method_is_not_a_hardcoded_zero` (fails against the
|
|
66
|
+
pre-fix code). The separate, correctly-implemented standalone
|
|
67
|
+
`chi_square()`/`chi_square_drift` (categorical drift) were never affected.
|
|
68
|
+
See `docs/ROADMAP_HONEST.md` for why a real continuous-data chi-square
|
|
69
|
+
statistic is a follow-up, not done in this pass.
|
|
70
|
+
- `engine::profiling::ProfilingEngine::profile_segment` returned three
|
|
71
|
+
hardcoded values — `separation: 0.5`, `health_score: size.min(100)`, and
|
|
72
|
+
`purity: 0.85` — despite its doc comment promising a real
|
|
73
|
+
stability/cohesion/separation health score. `separation` is now computed
|
|
74
|
+
against the complement (all non-member rows) per feature, `health_score` is
|
|
75
|
+
a bounded combination of the real stability/cohesion/separation
|
|
76
|
+
components, and `purity` is the true clustering purity when ground-truth
|
|
77
|
+
`labels` are passed (new optional argument) or a documented label-free
|
|
78
|
+
homogeneity proxy otherwise. Regression tests assert a well-separated
|
|
79
|
+
segment scores >0.9 separation, a full-coverage segment scores exactly
|
|
80
|
+
`0.0`, and the label purity case equals `0.75`.
|
|
81
|
+
- `engine::quality_metrics::StabilityAnalysis::adjusted_rand_index` was not
|
|
82
|
+
the Adjusted Rand Index at all — it returned `(RandIndex * 2.0 - 1.0)`,
|
|
83
|
+
which mis-scores even trivial inputs (e.g. `0.0` instead of `-0.5` for the
|
|
84
|
+
textbook two-pairings-of-four case). Replaced with the standard
|
|
85
|
+
contingency-table ARI; new tests cover the identical (`1.0`), hand-computed
|
|
86
|
+
`-0.5`, and partial-agreement cases.
|
|
6
87
|
|
|
7
88
|
## [7.3.1] - 2026-09-22
|
|
8
89
|
|