Imported from mapequation/infomap (
AGENTS.md). Install upstream withnpx skills add mapequation/infomap. Copyright stays with the author.
AGENTS.md
Mission
Keep changes small, source-aware, and easy to verify.
Default priorities:
- identify the smallest affected surface
- edit the real source-of-truth file
- run the smallest useful verification
- state clearly what you verified and what you did not
Do not bundle unrelated cleanup into the same change.
Repo Map
src/: C++ runtime behavior, algorithms, parsing, IO, and CLI semanticsinterfaces/python/: Python package sources, tracked SWIG outputs, and Sphinx sourceinterfaces/R/: R package skeleton (infomap/) and tracked SWIG-generated R outputs (generated/)interfaces/js/: JavaScript package sources and TypeScript sourcesinterfaces/swig/: SWIG interface files shared by Python and R bindingstest/: C++ doctest suites (test/cpp/), Python regression tests (test/python/), native benchmarks (test/bench/), and shared fixturesexamples/python/: executable Python examples used bymake test-pythonexamples/R/: executable R examples used bymake test-r-examples.github/workflows/: CI, release, and packaging workflows
Source Of Truth
src/owns runtime behaviorREADME.rstis the main repository and Python-docs landing-page sourceinterfaces/js/README.mdis the source README for the public npm packageinterfaces/python/source/owns the published Python docs sourceinterfaces/python/generated/andinterfaces/python/src/infomap/_swig.pyare tracked Python wrapper outputsscripts/generate_binding_options.py(make build-binding-options) generates the option surface from the C++ parameter catalog (src/io/ParameterCatalog.cpp+interfaces/parameters/overrides.json). The option surface is the whole ofinterfaces/python/src/infomap/_options.py, the marked signature block ininterfaces/python/src/infomap/_facade.py,interfaces/parameters/policy.md, and the R/TS option files. Edit the catalog sources (and the generator for cross-cutting changes). Then regenerate withmake build-binding-optionsand confirm withmake test-binding-options-freshness. Do not hand-edit the generated option code or docstringsinterfaces/R/infomap/owns the R package skeleton (R/,DESCRIPTION,tests/,man/)interfaces/R/generated/are tracked SWIG-generated R outputs; refresh withmake build-r-swig
When two documents disagree, fix the source document and regenerate the derived output; do not patch the generated copy by hand.
Bootstrap From Clean Clone
Start from a task branch, not master:
git clone https://github.com/mapequation/infomap.git
cd infomap
git switch -c fix/<short-task-name>
Baseline tools:
- Python 3.11 or newer; use
python3on macOS whenpythonis unavailable - R 4.0 or newer for the R package; CI exercises both
releaseandoldrel - Node.js 24 for the JavaScript worker package
gccorclangwith a working C++ toolchainswig4.4.1 only for refreshing tracked Python or R wrapper outputsem++from Emscripten 5.0.5 for JavaScript worker builds
Common local setup:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
make dev-bootstrap
make doctor
make build-native
make dev-bootstrap is the canonical setup step: it installs the Python
package with all extras (.[test,docs,examples,release]), runs npm ci, and
installs the pre-commit git hooks (make hooks). Run it inside the active
virtual environment.
To speed up the dependency install (the scientific extras are heavy), override
the installer with uv: make dev-bootstrap PIP="uv pip" (and likewise make dev-python-install PIP="uv pip"). uv pip
installs into the active virtual environment just like pip; only dependency
resolution is faster. CI already routes its Python installs through uv.
Use an active virtual environment for Python development. Some system-managed
Python installs, including Homebrew Python on macOS and distro Python on Linux,
reject direct pip install into the system environment.
On macOS with Homebrew:
brew install libomp swig
PATH="/opt/homebrew/bin:$PATH" make doctor
For Python wrapper refreshes, use SWIG 4.4.1 to match CI. For JavaScript
worker work, activate Emscripten 5.0.5 before you run make build-js or
make test-js.
Verification
Run the smallest sufficient verification for the changed surface:
src/changes:make build-native, then run the C++ tests withmake test-native- Python wrapper or packaging changes:
make build-python,make dev-python-install, andmake test-python - R wrapper or packaging changes:
make test-r(R CMD check) plusmake test-r-examples. Refresh tracked SWIG outputs withmake build-r-swigand verify withmake test-r-swig-freshness. Regenerate Rd/NAMESPACE withRscript -e 'roxygen2::roxygenise("interfaces/R/infomap")'after you install the package withR CMD INSTALL --with-keep.source. - JavaScript worker or package changes:
npm ciplusmake build-jsormake test-js - docs-only text changes: no code build needed; run
make build-docsto verify the site still builds - workflow or release changes: run the smallest relevant local smoke check and say what remains unverified
Before you commit, make test-fast runs a quick cross-cutting subset: the C++
stream-policy check, the C++ tests (make test-native), and the Python unit
tests (make test-python-unit).
Approximate local runtimes vary by machine and cache state:
make build-native: about 1-2 minutes on a clean checkoutmake build-python: about 2-4 minutes after Python dependencies are installedmake test-python: about 3-6 minutes after the package is built and installedmake test-r: about 1-3 minutes after R dependencies are installedmake build-js: about 2-4 minutes withem++already onPATHmake test-js: about 3-6 minutes after JavaScript dependencies and browsers are installedmake build-docs: about 1-3 minutes after Python docs dependencies are installed
Targeted checks:
- Single Python test:
make test-python-unit PYTEST_ARGS='test/python/test_x.py::test_y' - Python marker subset:
make test-python-unit PYTEST_ARGS='-m "not slow and not perf"' - Single JavaScript unit test:
npm run test:unit -- -t "name pattern" - JavaScript typecheck only:
npm run typecheck
Formatting And Linting
Pre-commit hooks mirror the CI lint gates and give the same feedback locally.
Install them once with make hooks (also run by make dev-bootstrap). On
commit they run ruff (Python lint), clang-format (C++ src/), biome
(JavaScript lint and format), air (R format), actionlint (GitHub
workflow YAML), and a C++ stream-policy check. At push time they also run
pyright on the core Python surface.
Format on demand without the hooks:
- C++:
make format-native(check only:make format-native-check) - Python:
make format-python - R:
make format-r(check only:make format-r-check) - JavaScript:
make format-js
CI enforces formatting through the pre-commit job, which runs the same
hooks locally and in CI — clang-format for C++ src/ and air for the R
sources. Format changes before you commit. Air is pre-1.0 and the repo
ships no air.toml, so its output is version-dependent. The canonical version
is 0.9.0, pinned in the pre-commit CI job via posit-dev/setup-air.
Install that version locally so make format-r-check agrees with CI, and bump
the CI pin and this note together. The actionlint hook is language: system
too: install it locally (brew install actionlint) when you touch workflows.
The canonical version is 1.7.12, downloaded in the pre-commit CI job, and
the CI pin and this note bump together. The R man pages are roxygen output and
its formatting is version-dependent too — 8.0.0 rewrites the whole R6 section —
so the canonical version is 7.3.3, pinned as R_ROXYGEN_VERSION in mk/r.mk
and as roxygen2@7.3.3 in the R CI job. make build-r-man refuses to run with
any other version, and the two pins and this note bump together. Every hook
excludes the generated and vendored files — interfaces/python/generated/,
interfaces/R/generated/, interfaces/python/src/infomap/_swig.py, and
vendor/. Never reformat them.
Environment
- Verify tool availability before use:
python,node,swig,em++, and the compiler toolchain - On some local macOS setups, Homebrew tools need
PATH="/opt/homebrew/bin:$PATH" - Never develop directly on
master; create or use a task-specific branch
Commits and Pull Requests
Commit subjects follow Conventional Commits: type(scope): summary, written
in imperative mood, lowercase after the colon, no trailing period.
release-please derives releases from them: feat bumps the minor version,
fix the patch version, and a BREAKING CHANGE: footer (or ! after the
type) the major version. Only feat, fix, perf, and revert commits
surface in the generated CHANGELOG.md. Never edit that file by hand.
Types and scopes are a fixed allowlist, restricted to what is frequent in this repo's history:
- Allowed types:
feat,fix,perf,docs,test,refactor,build,ci,chore. - Allowed scopes:
- no scope —
src/(C++ core) and repo-wide changes:feat: ...,fix: ...,perf: ... (python)— the Python interface (pyis a retired alias; do not use it)(R)— the R interface;(js)— the JavaScript interface(deps)/(deps-dev)— dependency bumps, dependabot's style
- no scope —
- Everything else requires explicit confirmation from the user before
committing: rare types (
style,revert), scopes that appear only occasionally in history (notebooks,docker,skills, ...), any breaking-change marker, and any type or scope not listed above. Never propose(core)— the C++ core is the unscoped default — and never writechore(master): release ...commits; those belong to release-please. - The user is the author of every commit and pull request. Author and commit all work in the user's name; never record yourself as the author.
- Never add yourself as a co-author. Do not append
Co-Authored-Bytrailers, "Generated with" footers, agent signatures, or session links to any commit or pull request. - Never mention yourself in commit messages, pull request titles, or pull request descriptions. Describe only the change and its rationale, written in the user's voice.
Do Not Guess
- Do not improvise around SWIG generation, Python packaging, or R packaging
- Do not improvise around JS worker generation or Emscripten
- Do not hand-edit
interfaces/R/generated/orinterfaces/R/infomap/man/; regenerate withmake build-r-swigandroxygen2::roxygenise(...)respectively - Do not make release or publishing changes casually
- Do not treat algorithmic, numerical, determinism, or memory issues as routine cleanup
Common Pitfalls
- macOS OpenMP: sometimes the compiler does not find an installed Homebrew
libomp. UsePATH="/opt/homebrew/bin:$PATH"and theCXXFLAGS/LDFLAGSrecipe inBUILD.md, or disable OpenMP withOPENMP=0for local smoke builds. - Stale Python extension: after you change C++ extension sources, SWIG
interfaces, or tracked wrapper outputs, rerun
make build-pythonandmake dev-python-installbefore Python tests. - SWIG drift: only refresh tracked Python or R wrapper outputs with SWIG
4.4.1, then run the matching freshness check
(
make test-python-swig-freshnessormake test-r-swig-freshness). - R 4.6 SWIG patch:
scripts/generate_r_swig.pypost-processes the SWIG output to replaceSET_S4_OBJECTand read-onlyCHARACTER_POINTERassignments removed in R 4.6, and addsR_useDynamicSymbols(dll, FALSE)toR_init_infomap. Tagged for removal once SWIG > 4.4.1 emits R 4.6-clean code (upstream PRswig/swig#3411). - R6 method discovery: roxygen2 only documents R6 methods defined inside
the
R6::R6Class()body. Adding methods later viaInfomapClass$set("public", ...)produces working code that does not appear in?InfomapClass. Define methods inline. - macOS R workaround:
mk/r.mkwrites a temporary Makevars pinning/usr/bin/clang++so the compiled R.sois libc++-compatible with Homebrew R.make doctorreports a missing/usr/bin/clang++. - Emscripten environment:
make build-jsandmake test-jsrequireem++from Emscripten 5.0.5 onPATH;npm cialone is not enough.
Repository Settings
Private vulnerability reporting, branch protection, required checks, stale approval dismissal, and linear-history requirements are GitHub repository settings. You cannot verify them from tracked files alone. Note any manual settings checks in the pull request when they affect the change.
Escalation
Stop and hand off when:
- the work spans multiple major surfaces
- the verification path is unavailable in the current environment
- the fix appears to require architectural redesign instead of a bounded patch
- you cannot validate the behavior change with a small scoped check
- the issue points to algorithm correctness, cross-platform divergence, or memory behavior