Contributing
This page details the guidelines to follow when contributing to ModifiedDistributions.jl.
Getting started
Before contributing, please:
Read the Getting started with Julia guide if you are new to Julia development
Install Task for the development workflows below:
macOS:
brew install go-task/tap/go-taskLinux: Download from releases or use your package manager
Windows:
winget install Task.Taskor download from releases
Check out the developer documentation for advanced workflows
Review the project structure and development commands below
Project structure
ModifiedDistributions.jl uses several environments for different purposes.
ModifiedDistributions.jl/
├── Project.toml # Main package environment
├── src/ # Package source
│ ├── ModifiedDistributions.jl # Module file: exports and centralised imports
│ ├── docstrings.jl # DocStringExtensions @template registration
│ ├── Affine.jl # affine modifier
│ ├── Weighted.jl # weight modifier
│ ├── Transformed.jl # series_transform / thin / cumulative
│ ├── Modified.jl # modify (hazard) modifier
│ ├── get_dist.jl # unwrap protocol
│ └── public.jl # public (non-exported) types
├── ext/ # Package extensions (ComposedDistributions)
├── test/
│ ├── Project.toml # Test environment
│ ├── runtests.jl # Main test entry (TestItemRunner)
│ ├── package/ # Quality gates (Aqua, ExplicitImports, JET, ...)
│ ├── ad/ # AD gradient harness (own environment)
│ ├── jet/ # JET static analysis (isolated environment)
│ ├── formatter/ # JuliaFormatter check (isolated environment)
│ └── ADFixtures/ # AD gradient scenario registry
├── docs/
│ ├── Project.toml # Documentation environment
│ ├── make.jl # Documentation build script
│ ├── pages.jl # Documentation navigation tree
│ └── src/ # Documentation source files
└── benchmark/
├── Project.toml # Benchmark environment
└── benchmarks.jl # Benchmark suite definitionFiles carrying a MANAGED by EpiAwarePackageTools.scaffold header are owned by the shared kit and rewritten on every sync. See Infrastructure and template sync for which files are managed and which are yours to edit.
Development commands
This project includes a Taskfile for the development workflows.
Quick start with tasks
# Discover all available tasks
task --list
# Common development workflow
task setup # One-time environment setup (all sub-environments)
task dev # Format + pre-commit + fast tests + fast docs
task precommit # Pre-commit validation
# Individual workflows
task test-fast # Quick testing (skips quality checks)
task docs # Full documentation build
task benchmark # Run benchmarksDetailed commands
For advanced usage, or when tasks don't cover a specific need, use the underlying Julia commands:
# Full test suite (recommended for CI and final checks)
julia --project=. -e 'using Pkg; Pkg.test()'
# Run tests directly (faster during development)
julia --project=test test/runtests.jl
# Skip quality tests for faster development iteration
julia --project=test test/runtests.jl skip_quality
# Run only the quality tests
julia --project=test test/runtests.jl quality_only
# Build complete documentation (includes Literate tutorial processing)
julia --project=docs docs/make.jl
# Execute the benchmark suite
task benchmarkTesting strategy
Test organisation
Unit tests: One file per modifier at the top of
test/(Affine.jl,Weighted.jl,Transformed.jl,Modified.jl,get_dist.jl) plus the extension testComposedDistributionsExt.jlQuality tests: Located in
test/package/, tagged:qualityAD gradient tests: Located in
test/ad/, tagged:ad, with their own environment and dedicated per-backend CI
Tests are @testitems discovered with TestItemRunner. The main entry (test/runtests.jl) accepts skip_quality, quality_only, and readme_only arguments to scope discovery, and excludes :ad-tagged items (they run from test/ad/runtests.jl).
Quality gates
The quality gates in test/package/ guard the package's health:
Aqua.jl: Common package issues (stale deps, ambiguities, piracy)
ExplicitImports.jl: No implicit or stale imports, and import centralisation
JET.jl: Static analysis for type stability (run from the isolated
test/jetenvironment)JuliaFormatter: Formatting check (run from the isolated
test/formatterenvironment)Docstring format: Every docstring matches the
src/docstrings.jltemplateDoctest and README section checks, plus an extension-ambiguity check
Run them all with task test-quality, or a single isolated gate directly:
task test-jet # JET static analysis
task test-formatting # JuliaFormatter checkAD gradient harness
Modifier log densities must differentiate cleanly across every backend the ecosystem uses. The harness in test/ad/ sweeps the scenarios registered in test/ADFixtures/ across six backends: ForwardDiff, ReverseDiff, Enzyme (reverse and forward), and Mooncake (reverse and forward), each an @testitem tagged for per-backend CI selection.
task test-ad # all backends
task test-ad-backend TAG=enzyme_reverse # a single backendEach scenario carries a ForwardDiff reference gradient the other backends are checked against. When you add a modifier, add a matching scenario to test/ADFixtures/src/ADFixtures.jl so the sweep covers it.
Documentation
Literate.jl tutorials
The tutorials are Literate.jl scripts located in docs/src/getting-started/tutorials/. These are converted to markdown during the documentation build.
Tutorials are plain Julia .jl files using md"""...""" blocks for markdown. You can run them directly in the REPL or as scripts.
- Adding a new tutorial:
Create a
.jlfile indocs/src/getting-started/tutorials/Add the generated
.mdfile todocs/pages.jl
- Tutorial format:
md"""
# Tutorial title
Introduction text.
"""
using ModifiedDistributions, Distributions
md"""
## Section
"""
affine(LogNormal(1.5, 0.5); scale = 2.0)Documentation structure
docs/src/getting-started/: User-facing documentationdocs/src/lib/: API documentation (auto-generated)docs/src/developer/: Developer and contributor documentation
Style guide
This project follows the SciML style guide.
Key points:
Use descriptive variable names
Follow Julia naming conventions (snake_case for variables, CamelCase for types)
Write docstrings for exported functions
Keep lines under 80 characters where possible
Use consistent indentation (4 spaces)
Documentation standards
All docstrings use the DocStringExtensions.jl @template conventions registered in src/docstrings.jl. That file is included near the top of the module, before any docstrings are defined, so the templates apply everywhere. The DocStringExtensions import itself lives in the module file (src/ModifiedDistributions.jl) rather than in docstrings.jl, because the kit's import-centralisation gate requires every import to sit in the module file.
Functions: Use $(TYPEDSIGNATURES) for automatic signature generation:
@doc "
$(TYPEDSIGNATURES)
Brief description of the function.
# Arguments
- `param1`: Description (no type annotations needed)
- `param2`: Description
# Keyword Arguments
- `kwarg1`: Description
"
function my_function(param1, param2; kwarg1 = default)
# implementation
endStructs: Use $(TYPEDEF) with inline field documentation, which $(TYPEDFIELDS) renders:
@doc "
$(TYPEDEF)
Description of the struct.
"
struct MyStruct
"Description of field1"
field1::Type1
"Description of field2"
field2::Type2
endKey rules:
Never use
@doc raw"- it bypasses the template systemDon't repeat type information in argument descriptions, since
$(TYPEDSIGNATURES)shows themUse
@doc "(not@doc """) to allow macro expansionDocument argument purpose, not types
Benchmarks
The suite in benchmark/benchmarks.jl reads each modifier's overhead against a bare base LogNormal. Evaluation groups follow the SUITE[<Type>][<variant>][<operation>] convention (construction, logpdf, pdf, cdf/ccdf, quantile, rand), and AD gradient rows are folded into a compact per-scenario-by-backend matrix using the "AD gradients" group convention (see benchmark/compare.jl).
task benchmark # benchmark the current state
task benchmark-compare # compare main vs current
task benchmark -- --filter=Modified # filter to specific benchmarksBenchmarks run on pull requests via .github/workflows/benchmark.yaml, and pushes to main record a timeline rendered on the Benchmarks documentation page.
Code quality
Pre-commit checklist
Before submitting a pull request:
- Run pre-commit checks (recommended):
task precommit- Or run individual checks:
task test # Full test suite
task docs-fast # Build documentationAdding new features
Write tests first: Add a test file for the modifier under
test/Implement the feature: Add the wrapper and verb in
src/, following Writing a new modifierAdd an AD scenario: Register a gradient scenario in
test/ADFixtures/Document the feature: Add docstrings and update documentation if needed
Test thoroughly: Run the full test suite
Getting help
Questions: Open a GitHub discussion
Bugs: File a GitHub issue with a minimal reproducible example
Feature requests: Open a GitHub issue with rationale and use case
General Julia help: See Julia Discourse or Julia Slack
Thank you for contributing to ModifiedDistributions.jl!