Imported from gencau/test-practices-agent-configurations (
dataset/repos/pennlinc§xcp_d/AGENTS.md). Install upstream withnpx skills add gencau/test-practices-agent-configurations --skill pennlinc§xcp_d. Copyright stays with the author.
AGENTS.md -- XCP-D
This file provides instructions for AI coding agents and human maintainers working on XCP-D, a BIDS App for robust postprocessing of fMRI data.
Shared Instructions (All PennLINC BIDS Apps)
The following conventions apply equally to qsiprep, qsirecon, xcp_d, and aslprep. All four are PennLINC BIDS Apps built on the NiPreps stack.
Ecosystem Context
- These projects belong to the NiPreps ecosystem and follow its community guidelines.
- Core dependencies include nipype (workflow engine), niworkflows (reusable workflow components), nireports (visual reports), pybids (BIDS dataset querying), and nibabel (neuroimaging I/O).
- All four apps are containerized via Docker and distributed on Docker Hub under the
pennlinc/namespace. - Contributions follow the NiPreps contributing guidelines.
Architecture Overview
Every PennLINC BIDS App follows this execution flow:
CLI (parser.py / run.py)
-> config singleton (config.py, serialized as ToML)
-> workflow graph construction (workflows/*.py)
-> Nipype interfaces (interfaces/*.py)
-> BIDS-compliant derivative outputs
- CLI (
<pkg>/cli/):parser.pydefines argparse arguments;run.pyis the entry point;workflow.pybuilds the execution graph;version.pyhandles--version. - Config (
<pkg>/config.py): A singleton module with class-based sections (environment,execution,workflow,nipype,seeds). Config is serialized to ToML and passed across processes via the filesystem. Access settings asconfig.section.setting. - Workflows (
<pkg>/workflows/): Built usingnipype.pipeline.engine(pe.Workflow,pe.Node,pe.MapNode). UseLiterateWorkflowfromniworkflows.engine.workflowsfor auto-documentation. Every workflow factory function must be namedinit_<descriptive_name>_wf. - Interfaces (
<pkg>/interfaces/): Custom Nipype interfaces wrapping external tools or Python logic. Follow standard Nipype patterns: define_InputSpec/_OutputSpecwithBaseInterfaceInputSpec/TraitedSpec, implement_run_interface(). - Utilities (
<pkg>/utils/): Shared helper functions. BIDS-specific helpers live inutils/bids.py. - Reports (
<pkg>/reports/): HTML report generation using nireports. - Data (
<pkg>/data/): Static package data (config files, templates, atlases). Accessed viaimportlib.resourcesor theacrespackage. - Tests (
<pkg>/tests/): Pytest-based. Unit tests run without external data. Integration tests are gated behind pytest markers and are skipped by default.
Workflow Authoring Rules
- Every workflow factory function must be named
init_<name>_wfand return aWorkflowobject. - Use
LiterateWorkflow(fromniworkflows.engine.workflows) to enable automatic workflow graph documentation. - Define
inputnodeandoutputnodeasniu.IdentityInterfacenodes to declare the workflow's external API. - Connect nodes using
workflow.connect([(source, dest, [('out_field', 'in_field')])])syntax. - Add
# fmt:skipafter multi-lineworkflow.connect()calls to prevent ruff from reformatting them. - Include a docstring with
Workflow Graphand.. workflow::Sphinx directive for auto-generated documentation. - Use
configmodule values (not function parameters) for global settings inside workflow builders.
Interface Conventions
- Input/output specs use Nipype traits (
File,traits.Bool,traits.Int, etc.). mandatory = Truefor required inputs; providedesc=for all traits.- Implement
_run_interface(self, runtime)-- neverrun(). - Return
runtimefrom_run_interface. - Set outputs via
self._results['field'] = value.
Config Module Usage
from <pkg> import config
# Read a setting
work_dir = config.execution.work_dir
# Serialize to disk
config.to_filename(path)
# Load from disk (in a subprocess)
config.load(path)
The config module is the single source of truth for runtime parameters. Never pass global settings as function arguments when they are available via config.
Testing Conventions
- Unit tests: Files named
test_*.pyin<pkg>/tests/. Must not require external neuroimaging data or network access. - Integration tests: Decorated with
@pytest.mark.<marker_name>. Excluded by default viaaddoptsinpyproject.toml. Require Docker or pre-downloaded test datasets. - Fixtures: Defined in
conftest.py. Common fixtures includedata_dir,working_dir,output_dir, anddatasets. - Coverage: Configured in
pyproject.tomlunder[tool.coverage.run]and[tool.coverage.report].
Documentation
- Built with Sphinx using
sphinx_rtd_theme. - Source files in
docs/. - Workflow graphs are auto-rendered via
.. workflow::directives that callinit_*_wffunctions. - API docs generated via
sphinxcontrib-apidoc. - Bibliography managed with
sphinxcontrib-bibtexandboilerplate.bib.
Docker
- Each app has a base image with runtime dependencies and a main Dockerfile that installs the Python environment and the app itself.
- ASLPrep, fMRIPrep, and XCP-D have migrated to Pixi-based multi-stage Docker builds:
Dockerfile.baseowns non-Python/non-conda runtime dependencies, whileDockerfileuses pixi to createbuild,test, and production targets frompixi.lock. qsiprep and qsirecon still use micromamba +pip install. - Base image naming: ASLPrep and XCP-D use date-based tags (
pennlinc/<pkg>-base:<YYYYMMDD>); qsiprep and qsirecon usepennlinc/<pkg>_build:<version>. - Entrypoint is the CLI binary in the pixi environment (e.g.,
/app/.pixi/envs/<env>/bin/<pkg>). - Labels follow the
org.label-schemaconvention.
Release Process
- Versions are derived from git tags via
hatch-vcs(VCS-based versioning). - GitHub Releases use auto-generated changelogs configured in
.github/release.yml. - Release categories: Breaking Changes, New Features, Deprecations, Bug Fixes, Other.
- Docker images are built and pushed via CI on tagged releases.
Code Style
- Formatter:
ruff format(target: all four repos). - Linter:
ruff checkwith an extended rule set (F, E, W, I, UP, YTT, S, BLE, B, A, C4, DTZ, T10, EXE, FA, ISC, ICN, PT, Q). - Import sorting: Handled by ruff's
Irule (isort-compatible). - Pre-commit: Uses
ruff-pre-commithooks for both linting and formatting. - Black is disabled:
[tool.black] exclude = ".*"in repos that have migrated to ruff.
BIDS Compliance
- All outputs must conform to the BIDS Derivatives specification.
- Use
pybids.BIDSLayoutfor querying input datasets. - Use
DerivativesDataSink(from the project's interfaces or niworkflows) for writing BIDS-compliant output files. - Entity names, suffixes, and extensions must match the BIDS specification.
Local Development
- Activate the environment defined in the
.cursor/cursorenvfile before running any commands.
XCP-D-Specific Instructions
Project Overview
XCP-D is a BIDS App for postprocessing fMRI data that has been preprocessed by fMRIPrep, nibabies, or similar pipelines. It handles:
- Confound regression and nuisance signal removal
- Temporal filtering (band-pass, motion filtering)
- Framewise displacement-based censoring (scrubbing)
- Despiking
- Smoothing
- Parcellation and connectivity matrix generation
- Surface processing (CIFTI/GIFTI workflows)
- Concatenation of runs within task entity sets
- Executive summary HTML report generation
- Data ingression from non-BIDS formats (ABCD-BIDS, HCP-YA, UK Biobank)
Repository Details
| Item | Value |
|---|---|
| Package name | xcp_d |
| Default branch | main |
| Entry point | xcp_d.cli.run:main |
| Python requirement | >=3.10 |
| Build backend | hatchling + hatch-vcs + nipreps-versions |
| Linter | ruff ~= 0.15.0 |
| Pre-commit | Yes (ruff v0.6.2) |
| Tox | Yes |
| Docker base | pennlinc/xcp_d-base:<date> |
| Dockerfile | Pixi-based multi-stage (build, test, xcp_d) |
Key Directories
xcp_d/workflows/bold/: BOLD postprocessing workflows for NIfTI and CIFTI data, plus run concatenationxcp_d/workflows/anatomical/: Anatomical postprocessing (surfaces, volumes, parcellation)xcp_d/workflows/parcellation.py: Atlas loading and parcellation workflowxcp_d/workflows/plotting.py: QC plot generation workflowsxcp_d/interfaces/: Nipype interfaces for censoring, connectivity, concatenation, ANTs, nilearn, workbench, executive summary, plottingxcp_d/ingression/: Modules for ingressing data from non-BIDS formats (ABCD-BIDS, HCP-YA, UK Biobank)xcp_d/data/atlases/: Bundled brain atlases (Glasser, Gordon, HCP, Tian, MIDB, MyersLabonte)xcp_d/data/nuisance/: YAML configs for nuisance regression strategiesxcp_d/data/executive_summary_templates/: Jinja2 HTML templates for the executive summary report
Version Management
XCP-D uses __about__.py for version metadata:
from xcp_d.__about__ import __copyright__, __credits__, __packagename__, __version__
This is different from qsiprep/qsirecon which import __version__ directly from _version.py. Both patterns work; harmonization is a roadmap item.
Linting Status (Reference for Other Repos)
XCP-D has the cleanest ruff configuration of all four repos, with only 3 suppressed rules:
S105: Hardcoded password detection (false positives)S311: Random not for crypto (intentional)S603: Subprocess with shell=True (trusted commands only)
This minimal ignore set is the target for the other three repos.
Ingression Modules
xcp_d/ingression/ contains adapters for non-BIDS input formats:
abcdbids.py: ABCD-BIDS formathcpya.py: HCP Young Adult formatukbiobank.py: UK Biobank formatutils.py: Shared ingression utilities
When adding support for new input formats, create a new module in this directory following the existing patterns.
Executive Summary
XCP-D generates rich HTML executive summary reports using Jinja2 templates in xcp_d/data/executive_summary_templates/. These include:
- BrainSprite interactive brain viewers
- Anatomical registration quality plots
- Task-specific static QC plots
The xcp_d/interfaces/execsummary.py interface and xcp_d/utils/execsummary.py utilities handle generation.
fill_doc Decorator
XCP-D uses a @fill_doc decorator (from xcp_d.utils.doc) to inject shared parameter documentation into function docstrings. Use it on any workflow init function that accepts standard parameters documented in the shared parameter dictionary.
Config Hashing
XCP-D implements config hashing (config.hash_config()) to create unique identifiers for pipeline configurations. This is used to detect when outputs need to be regenerated due to parameter changes.
Cross-Project Development Roadmap
This roadmap covers harmonization work across all four PennLINC BIDS Apps (qsiprep, qsirecon, xcp_d, aslprep) to reduce maintenance burden.
Phase 1: Bring qsirecon to parity
- Migrate qsirecon from flake8+black+isort to ruff -- copy the
[tool.ruff]config from xcp_d'spyproject.tomland remove[tool.black],[tool.isort],[tool.flake8]sections. - Add
.pre-commit-config.yamlto qsirecon -- identical to the config used by qsiprep, xcp_d, and aslprep. - Add
tox.inito qsirecon -- copy from qsiprep or xcp_d (they are identical). - Add
.github/dependabot.ymlto qsirecon. - Reformat qsirecon codebase -- run
ruff formatto switch from double quotes to single quotes.
Phase 2: Standardize across all four repos
- Rename qsiprep default branch from
mastertomainand update.github/workflows/lint.yml. Rename aslprep test extras from-- Done.testtotests- Converge on version management -- recommend the simpler
_version.pydirect-import pattern (used by qsiprep/qsirecon). ASLPrep has completed this migration; only xcp_d still uses__about__.py. - Pin the same ruff version in all four repos' dev dependencies and
.pre-commit-config.yaml. - Harmonize ruff ignore lists -- adopt xcp_d's minimal set (
S105,S311,S603) as the target; fix suppressed rules in qsiprep and aslprep incrementally.
Phase 3: Shared infrastructure
- Extract a reusable GitHub Actions workflow for lint + codespell + build checks, hosted in a shared repo (e.g.,
PennLINC/.github). - Standardize Dockerfile patterns -- ASLPrep, fMRIPrep, and XCP-D have adopted pixi-based multi-stage Docker builds. Migrate qsiprep and qsirecon to the same pattern.
- Create a shared
pennlinc-stylepackage or cookiecutter template providingpyproject.tomllint/test config,.pre-commit-config.yaml,tox.ini, and CI workflows. - Evaluate
nipreps-versionscalver -- theraw-options = { version_scheme = "nipreps-calver" }line is commented out in all four repos. Decide whether to adopt it.