Imported from gammasim/simtools (
AGENTS.md). Install upstream withnpx skills add gammasim/simtools. Copyright stays with the author.
simtools Agent Guide
This file gives repo-wide instructions for AI agents working on simtools.
simtools is a Python toolkit for CTAO Monte Carlo production support: model parameter handling, MongoDB access, CORSIKA and sim_telarray configuration, application workflows, validation, reporting, and plotting.
In general, do not pretend you are a human developer. You are a tool, you don't think. If given a task, follow the instructions and do not make assumptions. Just do what you are told. If you are unsure, ask for clarification. Try to shut up.
First Steps
- Inspect the existing code and tests before changing behavior.
- Prefer the narrowest change that fixes the requested issue.
- Make architectural changes for the long term. Avoid short-term hacks.
- Check whether a local skill applies:
.agents/skills/unit-testing/SKILL.md.agents/skills/integration-testing/SKILL.md.agents/skills/documentation/SKILL.md
- Keep generated or unrelated user changes intact. Do not revert files you did not intentionally modify.
Project Facts
- Python:
>=3.14frompyproject.toml. - Python 3.14 allows multiple exception types without parentheses (PEP 758) when not using
as, e.g.except AttributeError, KeyError, TypeError:; use parentheses forexcept (AttributeError, KeyError, TypeError) as exc:. - Source package:
src/simtools/. - Applications:
src/simtools/applications/, installed assimtools-*commands through[project.scripts]. - Unit tests:
tests/unit_tests/, mirroringsrc/simtools/. - Integration tests:
tests/integration_tests/config/*.yml, executed throughtests/integration_tests/test_applications_from_config.py. - Test resources: versioned integration resources in
simtools-tests; ordinary unit tests must not depend on repository-local or external resource files. - Documentation: Sphinx in
docs/source/, built from MyST Markdown plus some RST autodoc pages. - Changelog fragments:
docs/changes/<pr-number>.<type>.md.
Development Conventions
- Use
pathlibfor paths. - Do not add
__init__.pyfiles; this project uses implicit namespace packages. - Use double quotes for strings and docstrings.
- Use f-strings for formatting.
- Use logging for user/developer messages; do not use
printin library code. - Documentation and comments should describe current behavior, not historical production details or changes from earlier behavior.
- Put useful exception text in the raised exception. When wrapping exceptions,
use
raise ... from exc. - Avoid
logger.errorimmediately before raising; it usually duplicates the failure. - Use
astropy.unitsfor physical quantities. - Validate CTAO names through existing helpers in
simtools.utils.names. - When introducing a new schema version in
src/simtools/schemas, add a new YAML document and preserve the existing version; do not replace it. - Use semantic model versions without a leading
vin new configs. - Do not add type hints to function signatures unless the surrounding module already deliberately uses them.
- Keep cognitive complexity below 15; extract private helpers before adding deeply nested logic.
- Keep code and docs ASCII-only unless an existing file clearly requires another character set.
Container Workflows
- If a task requires building or running containers, first check whether Docker, Podman, or Apptainer is available and use an available runtime where practical.
- Do not install or require a container runtime without explicit user approval.
Python Environment
- If
python,pytest, orpylintis not available in the current environment, check for a Conda or Mamba environment namedsimtools-devand run the command there when available.
Testing
Default pytest runs unit tests only because tool.pytest.ini_options.testpaths
is tests/unit_tests/.
Use focused commands first:
pytest tests/unit_tests/path/to/test_module.py
pytest -vv tests/unit_tests/path/to/test_module.py::test_name
pytest --durations=10 tests/unit_tests/
Run broader checks when shared behavior changes:
pytest
pytest --cov=simtools --cov-report=term-missing
pre-commit run --all-files
Unit-test rules:
- Use plain pytest functions, not test classes.
- Cover changed success paths, error paths, and branches.
- Every Python module under
src/simtools/must have a matching unit-test file undertests/unit_tests/, preserving its package directory and using thetest_<module>.pyname, except for the modules excluded by CI:__init__.py,_version.py, and anything underapplications/. This includes small helper modules such assimtel/pulse_shapes.pyandutils/value_conversion.py; do not leave them uncovered or rely on tests of a neighboring module. - Use local fixtures first; use
tests/unit_tests/conftest.pyfor fixtures shared across unit-test modules. - Shared repo fixtures such as
test_resources_pathandsimtools_root_pathlive intests/conftest.py. - Do not make ordinary unit tests depend on checked-in, downloaded, or external
files. If file parsing or writing is the behavior under test, generate the
smallest valid input in
tmp_test_directorywithin the test. - Keep file-format compatibility and resource-heavy checks in integration tests.
- Use
tmp_test_directoryfor file I/O. Do not introduce hardcoded/tmp,tempfile, or absolute temporary paths in tests. - Mock databases, network calls, file I/O, CORSIKA, and sim_telarray in unit tests unless the test is explicitly marked for external resources.
- Use
pytest.approx()for floats andastropy.tests.helper.assert_quantity_allclosefor quantities. - Warnings are treated as errors; fix deprecations instead of filtering them unless there is a clear project-wide reason.
Integration Tests
Integration tests run real simtools-* applications from YAML configs. Use the
integration-testing skill for config work.
Important mechanics:
- Schema:
src/simtools/schemas/application_workflow.metaschema.yml. - Config shape starts with
applications:and ends withschema_name: application_workflow.metaschemaandschema_version: 0.4.0. model_version_use_current: trueis lower-case and application-level.- Generated paths such as
output_path,grid_output_path, andpack_for_grid_registershould be relative; the harness rewrites them intotmp_test_directory. - Set
SIMTOOLS_TESTS_PATHandSIMTOOLS_TESTS_TAG, or use--simtools_tests_tag, to select a taggedsimtools-testsresource bundle whenSIMTOOLS_TEST_RESOURCESor--test_resources_pathis not provided. - Use
${static:path/to/file}for maintained resources and${generated:path/to/file}for generated resources. Pytest resolves these against--test_resources_path, defaulting toSIMTOOLS_TEST_RESOURCESor--test_resources_path. - Put
expected_sim_telarray_outputandexpected_sim_telarray_metadatadirectly on the relevanttest_output_filesitem. - Use
test_simtel_cfg_filesfor version-specific sim_telarray cfg comparisons. - Add
docs.titleanddocs.summarywhen the config should appear as a rendered documentation example.
Useful commands:
pytest --no-cov tests/integration_tests/test_applications_from_config.py
pytest -v -k "simtools-<app-name>" tests/integration_tests/test_applications_from_config.py
pytest -v -k "simtools-<app-name>_<test_name>" \
tests/integration_tests/test_applications_from_config.py
pytest -v --model_version 6.0.2 -k "<test_name>" \
tests/integration_tests/test_applications_from_config.py
pytest -v --test_resources_path /full/path/to/resources \
tests/integration_tests/test_applications_from_config.py
Integration tests often require .env MongoDB settings and installed CORSIKA /
sim_telarray. Unit tests should not.
Documentation
Use the documentation skill for docs, API reference, changelog, and docstring work.
- Documentation pages are preferred in MyST Markdown.
- Application autodoc pages are small RST files in
docs/source/user-guide/applications/. - New application pages use one
.. automodule::directive with:members:and usually:exclude-members: main. - Add new application pages to
docs/source/user-guide/applications.mdin alphabetical order. - Add new library modules to the relevant
docs/source/api-reference/*.mdfile with.. automodule:: <module.path>and:members:. - Public functions, classes, and methods need NumPy-style docstrings. Include
only relevant sections such as
Parameters,Returns,Raises, andExamples. - Changelog fragment types:
feature,bugfix,api,doc,maintenance,model. Use the PR number, not the issue number. - Do not add development plans to the documentation.
Docs validation:
cd docs
make clean
make html
make linkcheck
Before handing off a change that adds, removes, or moves a library module, run
the API coverage check as well. Every reported module must be added to the
appropriate API reference page with an automodule entry for its complete
simtools.* import path:
FULLY_DOCUMENTED="TRUE"
MODULES=$(find src/simtools \
\( -path "src/simtools/applications" -o -path "src/simtools/_*" \) -prune \
-o -type f -name "*.py" ! -name "__init__.py" -print)
for module_path in $MODULES; do
module=$(basename "$module_path" .py)
if ! grep -q "$module" docs/source/api-reference/*.md; then
echo "Undocumented module: $module"
FULLY_DOCUMENTED="FALSE"
fi
done
if [[ "$FULLY_DOCUMENTED" = "FALSE" ]]; then
exit 1
fi
Adding Code
New application checklist:
- Add the application under
src/simtools/applications/. - Register the command in
[project.scripts]inpyproject.toml. - Add or update unit tests.
- Add an integration config in
tests/integration_tests/config/. - Add the RST application page and applications toctree entry.
- Add a changelog fragment when working in a PR flow.
- Applications in
src/simtools/applications/are entry scripts. If possible, avoid adding code here and instead put reusable code in a library module.
New library module checklist:
- Add focused unit tests under the mirrored
tests/unit_tests/path. - Add API reference documentation.
- Add or update user documentation if behavior is user-facing.
- Add a changelog fragment when working in a PR flow.
Before handing off a change that adds, removes, or moves a library module, run the same source-to-test check used by CI and resolve every reported path:
python - <<'PY'
from pathlib import Path
src_root = Path("src/simtools")
test_root = Path("tests/unit_tests")
missing = []
for path in src_root.rglob("*.py"):
relative = path.relative_to(src_root)
if path.name in {"__init__.py", "_version.py"} or relative.parts[0] == "applications":
continue
if not (test_root / relative.parent / f"test_{path.stem}.py").exists():
missing.append(str(relative))
if missing:
raise SystemExit("Modules without unit tests:\n" + "\n".join(sorted(missing)))
PY
Linting And Formatting
Pre-commit runs ruff, ruff-format, pylint, flake8 cognitive complexity, docstring coverage, actionlint, pyproject-fmt, codespell, markdownlint, yamllint, towncrier, and shellcheck.
Useful commands:
ruff check --fix
ruff format
pylint src/simtools/path/to/module.py
pre-commit run --all-files
Pylint excludes tests. Do not satisfy pylint by adding broad disables when a small refactor or better naming fixes the issue.
Domain Conventions
- Sites:
North,South. - Telescope names: examples include
LSTN-01,LSTS-01,MSTN-01,MSTS-05; checksrc/simtools/resources/array_elements.yml. - Array layout names and telescope names vary by model version; use
by_versionin integration configs where needed. - Model-parameter schema changes can affect sim_telarray metadata. If an integration failure says a required metadata key is missing, inspect the relevant schema, DB/mock parameter data, and sim_telarray metadata registry before changing the test expectation.
Recurring Failure Checks
These issues have appeared repeatedly in local Codex logs and CI snippets:
- Missing generated/static test resources: prefer
${static:...}and${generated:...}over rawtests/resources/...paths in integration configs. - Wrong output descriptor in integration checks: verify whether the file is
under
output_path,grid_output_path, orpack_for_grid_register. - sim_telarray files not produced: inspect the application log path printed in stderr before changing expected filenames.
log_inspectorfailures: look for realerror,exception,traceback,failed,runtime warning, orsegmentation faulttext in stdout/stderr.- Warnings-as-errors failures: update deprecated APIs, for example matplotlib colormap handling, rather than suppressing the warning locally.
- Pylint duplicate-code or complexity failures: extract a helper only when it improves readability and matches local patterns.
Undocumented moduleCI failures: add the missing API reference entry.- The API documentation check scans every non-application, non-private
src/simtools/**/*.pymodule by basename. When adding or moving a module, ensure that its basename is present in the appropriatedocs/source/api-reference/*.mdpage, with anautomoduledirective for the complete import path. Resolve everyUndocumented module: <name>result; do not silence the check or rely on a partial documentation build.