Guidance for AI coding agents (Claude Code, Codex, Cursor, ...) working in this repository. Humans should start from README.md and docs/CONTRIBUTING.md; this file is the condensed, agent-oriented version and the single source of truth for agent instructions.
dataprof is a local, deterministic data profiling and quality assessment
library: a Rust workspace with a PyO3-based Python package on top. The release
surface is Rust library crates plus Python wheels — there is no CLI binary.
It profiles data and reports on it; it does not transform, clean, or move data.
Keep changes within that identity.
examples/ are executable docs: CI runs them, so keep them working.
Prerequisites: Rust 1.96+, Python 3.11+, uv.
Local development runs on CPython 3.15, pinned in .python-version, which uv
reads and installs on its own; ty still checks against the 3.11 floor.
1.96 is the MSRV (rust-version in Cargo.toml). CI lints and tests on the
toolchain pinned in rust-toolchain.toml, and rustup reads that file, so plain
cargo in this checkout runs the same compiler as CI. Clippy gates the build
with -D warnings and every Rust release adds lints; a cargo +<version>
command or a RUSTUP_TOOLCHAIN variable overrides the file, so when clippy
disagrees with CI, check rustc --version first.
The MSRV itself is enforced by compilation, not by the declaration: the MSRV Compile Gate job runs cargo check --locked --lib over every published feature
graph on 1.96. Reproduce it with
RUSTUP_TOOLCHAIN=1.96 uv run --no-project python .github/scripts/msrv_check.py. If a change needs
a newer standard-library API, raising the MSRV is a deliberate separate change —
see docs/CONTRIBUTING.md.
uv sync --no-install-project # the Python dev environment, without building dataprof
uv run --no-sync maturin develop # build + install the local Python extension
# Focused tests (prefer these; match the command to your change)
cargo test -p dataprof-core
cargo test -p dataprof-python
uv run --no-sync pytest python/tests/test_python_api.py -q
# Gates that CI enforces — run before committing
cargo fmt --all
cargo clippy --all --all-targets -- -D warnings
uv run --no-sync ruff format python/ .github/scripts/ .claude/skills/dataprof/scripts/
uv run --no-sync ruff check python/ .github/scripts/ .claude/skills/dataprof/scripts/
uv run --no-sync ty check python/Plain uv sync also builds the extension once in release mode, and plain
uv run rebuilds it after maturin develop: keep --no-install-project and
--no-sync as above, which is what CI runs. A later uv sync uninstalls the
extension again, so run maturin develop after it.
Don't run every expensive workspace check for a small docs or Python-only change; run what matches the change and list the commands in the PR body.
- Commits: conventional commits (
feat:,fix:,docs:,ci:,chore:, with optional scope likefix(python):). Release notes are generated by git-cliff from commit subjects, so a non-conventional subject can silently vanish from the changelog. Keep messages brief. Do not add AI attribution trailers (Co-Authored-By: Claude, "Generated with..."). - Branches:
feature/...,fix/...,docs/...,perf/.... PRs targetmaster. One behavior change per PR; no drive-by refactors. - Error handling: never swallow decode/parse errors into defaults
(
.ok(),unwrap_or_default()on decode paths). A silent error that turns into a plausible number is the worst bug class for a profiler. - Semantics:
None/absent means "not analyzed"; empty means "analyzed, nothing found". Preserve this distinction in reports and bindings. - Output contract: the serialized, rounded metric values must be identical
across engines and input paths for the same logical data, analysis options,
and analyzed population. Rust Serde output and Python
to_dict()/to_json()govern this contract; source/execution provenance is compared separately. Native raw floats retain full precision and may differ with accumulation order. Parity tests compare serialized values exactly; raw diagnostics use explicit tolerances (relative1e-9, absolute1e-12). Preserve absence as well as numbers. Values nearf64's limits are an approximate domain where these results may differ; the contract defines its bounds. See the numeric contract. If you touch an engine or parser, check the other paths for parity. - PR descriptions: derive behaviour from the code the issue names, not from the issue text alone. Every sentence of the description must be true of the diff, and each acceptance criterion of the issue is either met (say where) or stated as left out. A fix lists its class of defect (values, routes, report forms) as covered or left out, in the template's section. See Understanding Your Change.
- Milestones are releases only (e.g.
0.9.1). Do not create thematic milestones. - Themes/initiatives live in epic issues (e.g. "Epic: Agent & LLM Integration"); link related issues from the epic body.
- No version-specific labels; use
area: *andtype: *labels.
- docs/CONTRIBUTING.md — full contributor workflow.
- docs/release-notes.md — what the latest published release shipped, including breaking changes. It is rewritten at release time from the merged PRs, so do not edit it in a feature or fix PR, and do not ask for an entry in review: an unreleased change added there is credited to a release that does not contain it. Describe behaviour changes, and any re-baselining they require, in the PR description instead.
- docs/SECURITY.md — supported versions, reporting.