Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .agent-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ Goal: ship a best-in-class educational synthetic CRM lead-scoring dataset family
- [x] PR 2.2: `check_exposure_monotonicity` updated for v5 — student is allowed to omit `BANNED_TABLES`, drop `BANNED_LEAD_COLUMNS` / `BANNED_OPP_COLUMNS`, and be a row-subset of instructor on snapshot-filtered event tables.

### Phase 3 — Release validation hardening
- [ ] `leadforge/validation/{release_quality,leakage_probes,reporting}.py` (new)
- [ ] `scripts/validate_release_candidate.py` (new)
- [ ] Resolve numeric `TBD-*` bands in `v1_acceptance_gates.md`
- [ ] `release/validation/validation_report.{json,md}` + figures auto-generated
- [x] PR 3.1: `leadforge/validation/leakage_probes.py` (new) — unified leakage taxonomy. Subsumes the PR 2.1 `relational_leakage` module and broadens it to the full design-doc / acceptance-gates taxonomy: direct (banned columns / banned tables, generalised to accept caller-supplied banned sets), time-window (`probe_snapshot_window`, generalised over `(table, ts_col)` pairs), relational (`probe_deterministic_reconstruction`, `deterministic_relational_reconstruction`), split (`probe_split_id_overlap` for G6.1/G6.2, `probe_split_near_duplicates` via deterministic rounded-vector hashing for G6.3, `probe_split_label_drift` opt-in), model-realism (`probe_bonus_model_auc` opt-in, new opt-in `probe_id_only_baseline` for G5.3, `probe_feature_subset_baseline` for G5.1/G5.2). `PROBE_REGISTRY` is the single source of truth (probe → taxonomy / opt-in flag); meta-test asserts every module-level `probe_*` is registered. Two orchestrators: `run_all_probes` / `run_all_probes_on_dataframes` (structural, kept stable for `validate_bundle`) and new `run_split_probes` (split-level over `{split_name: DataFrame}`). `relational_leakage.py` deleted; every internal call site updated (`leadforge/validation/{bundle_checks,invariants}.py`, `leadforge/render/{manifests,relational_snapshot_safe}.py`, `leadforge/exposure/filters.py` doc, `scripts/probe_relational_leakage.py`); test file renamed `test_relational_leakage.py` → `test_leakage_probes.py` and grew 24 new tests for the new probes + meta-coverage. `RelationalLeakageError` retained (now spans every taxonomy) with `LeakageError` alias for the new umbrella name. `BUNDLE_SCHEMA_VERSION` unchanged (purely additive on the validator side); 1067/1067 tests pass; hash-determinism preserved (67/67 files identical); `scripts/probe_relational_leakage.py release/{intro,intermediate,advanced} --max-accuracy 0.65` exits 0 on every public tier.
- [ ] PR 3.2: `leadforge/validation/{release_quality,reporting}.py` (new)
- [ ] PR 3.3: `scripts/validate_release_candidate.py` (new); resolve numeric `TBD-*` bands in `v1_acceptance_gates.md`; `release/validation/validation_report.{json,md}` + figures auto-generated

### Phase 4 — Channel-signal audit + dataset card hardening
- [ ] `scripts/audit_channel_signal.py` → `docs/release/channel_signal_audit.md`
Expand Down
6 changes: 3 additions & 3 deletions leadforge/exposure/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ class BundleFilter:
must be projected onto the snapshot-safe shape before being
written. When ``True``, the bundle writer routes through
:func:`leadforge.render.relational_snapshot_safe.to_dataframes_snapshot_safe`,
which strips :data:`leadforge.validation.relational_leakage.BANNED_LEAD_COLUMNS`
from ``leads``, :data:`~leadforge.validation.relational_leakage.BANNED_OPP_COLUMNS`
which strips :data:`leadforge.validation.leakage_probes.BANNED_LEAD_COLUMNS`
from ``leads``, :data:`~leadforge.validation.leakage_probes.BANNED_OPP_COLUMNS`
from ``opportunities``, filters event tables per-lead by
``lead_created_at + snapshot_day``, and omits
:data:`~leadforge.validation.relational_leakage.BANNED_TABLES`
:data:`~leadforge.validation.leakage_probes.BANNED_TABLES`
(``customers`` / ``subscriptions``) entirely. When ``False``,
the writer emits the full-horizon export.
"""
Expand Down
2 changes: 1 addition & 1 deletion leadforge/render/manifests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from typing import TYPE_CHECKING, Any

from leadforge.core.hashing import file_sha256
from leadforge.validation.relational_leakage import (
from leadforge.validation.leakage_probes import (
BANNED_LEAD_COLUMNS,
BANNED_OPP_COLUMNS,
BANNED_TABLES,
Expand Down
4 changes: 2 additions & 2 deletions leadforge/render/relational_snapshot_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
The ``research_instructor`` mode keeps using
:func:`leadforge.render.relational.to_dataframes` for the full-horizon
export. The contract constants live in
:mod:`leadforge.validation.relational_leakage` (validator owns the
:mod:`leadforge.validation.leakage_probes` (validator owns the
definition of "leakage"); this module re-exports them for ergonomics.
"""

Expand All @@ -34,7 +34,7 @@

import pandas as pd

from leadforge.validation.relational_leakage import (
from leadforge.validation.leakage_probes import (
BANNED_LEAD_COLUMNS,
BANNED_OPP_COLUMNS,
BANNED_TABLES,
Expand Down
10 changes: 5 additions & 5 deletions leadforge/validation/bundle_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
from leadforge.schema.features import LEAD_SNAPSHOT_FEATURES, redacted_columns_for
from leadforge.schema.relationships import ALL_CONSTRAINTS
from leadforge.validation.difficulty import check_difficulty
from leadforge.validation.realism import check_realism
from leadforge.validation.relational_leakage import (
from leadforge.validation.leakage_probes import (
BANNED_TABLES,
LeakageReport,
probe_banned_columns,
probe_banned_tables,
probe_deterministic_reconstruction,
run_all_probes,
)
from leadforge.validation.realism import check_realism


def validate_bundle(bundle_root: Path, *, include_realism: bool = True) -> list[str]:
Expand Down Expand Up @@ -311,7 +311,7 @@ def _check_relational_leakage(root: Path, manifest: dict[str, Any]) -> list[str]
when ``snapshot_day`` is unavailable, with an explicit error
surfaced so the gap is visible.

Each :class:`~leadforge.validation.relational_leakage.LeakageFinding`
Each :class:`~leadforge.validation.leakage_probes.LeakageFinding`
is rendered as one error string, keeping the existing
``validate_bundle`` contract (return list of strings, empty = pass).
"""
Expand Down Expand Up @@ -354,10 +354,10 @@ def _read_relational_tables(root: Path) -> dict[str, pd.DataFrame]:
"""Read every public + banned-table parquet under ``<root>/tables/``.

Mirrors the read logic in
:func:`leadforge.validation.relational_leakage.run_all_probes` but
:func:`leadforge.validation.leakage_probes.run_all_probes` but
is reusable for the snapshot_day-missing path above.
"""
from leadforge.validation.relational_leakage import (
from leadforge.validation.leakage_probes import (
BANNED_TABLES as _BANNED,
)

Expand Down
2 changes: 1 addition & 1 deletion leadforge/validation/invariants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from leadforge.core.hashing import file_sha256
from leadforge.render.manifests import NON_DETERMINISTIC_MANIFEST_FIELDS
from leadforge.schema.features import redacted_columns_for
from leadforge.validation.relational_leakage import (
from leadforge.validation.leakage_probes import (
BANNED_LEAD_COLUMNS,
BANNED_OPP_COLUMNS,
BANNED_TABLES,
Expand Down
Loading
Loading