Onboarding notes for contributors and coding agents. Everything here is something you would otherwise discover the hard way — most of these failures are silent.
This is the canonical file; CLAUDE.md and .github/copilot-instructions.md just point here.
Layout
| Path | What it is |
|---|---|
R/ |
package source (~44k lines). class-*.R define the S4 classes |
glpk/ gams/ julia/ pyomo/ |
solver model templates — not shipped as files, see below |
data-raw/ |
scripts that build R/sysdata.rda and data/*.rda
|
dev/, dev-scripts/
|
design notes and maintenance utilities |
depreciated/, drafts/
|
archived code — do not edit |
docs/ |
pkgdown build output — never edit by hand |
Build and test
Two traps:
-
devtools::test()aborts the whole run withunexpected inputat1:1— the filetests/testthat/test-mapping-engine.Rstarts with a UTF-8 BOM (EF BB BF). Usedevtools::test(filter = "...")to work around it, and note that a “clean” run that stopped early is not a passing run. -
Running a test file directly silently skips solver tests.
skip_if_no_solver()begins withskip_on_cran(), and a bareRscripthas noNOT_CRAN.devtools::test()sets it; if you calltestthat::test_file()yourself, setNOT_CRAN=true. Symptom is a suspiciously green run (e.g. 5 pass / 7 skip instead of 28 pass).
Traps that fail silently
new() does not construct objects. new("technology", name = "X") returns an empty technology — no error. Sixteen no-op initialize methods (function(.Object, ...) .Object) deliberately disable S4’s argument filling. The real constructor is R/data2slots.R, reached via newTechnology(), newCommodity(), newSupply(), … Use those. update() on an existing object goes through the same path.
S3 methods on S4 classes need @exportS3Method. print is an S4 generic whose default body is UseMethod("print") (R/print.R:11). A #' @export tag on print.<class> produces a plain export() in NAMESPACE, which does not register the method — dispatch falls through to the S4 default and dumps raw slots. Use #' @exportS3Method print <class>. Currently dead for this reason: print.commodity, print.demand, print.supply.
Editing a solver template has no effect until you rebuild sysdata. glpk/, gams/, julia/ and pyomo/ are in .Rbuildignore; the templates ship only as character vectors baked into R/sysdata.rda by data-raw/DATASET.R. After changing any template — or data-raw/maps.R or data-raw/modInp.yml — re-run data-raw/DATASET.R from the package root (it sources maps.R first and all its paths are relative) and commit the regenerated R/sysdata.rda.
The templates are spliced at grep-located markers. Writers in R/write_glpk.R, R/write_gams.R, R/write_jump.R and R/write_pyomo.R locate split points by regex, and some hard-stop unless the match is unique. When editing a template, do not introduce a line matching ^minimize or ^end; (GLPK), $include data.gms (GAMS), ^##### decl par ##### or ^model.obj (Pyomo), or ^@objective (Julia). glpk/energyRt.mod also contains a UUID marker on line ~529 that must stay unique.
devtools::document() touches far more than your change. It rewrites every .Rd and can flush a backlog of stale docs from unrelated in-flight work. Always check git diff NAMESPACE and git status man/ before committing.
Collate: in DESCRIPTION is load-bearing. S4 requires parent classes to exist at setClass time. New class files need an @include tag so roxygen orders them correctly.
Changing a class slot breaks saved scenarios. save_scenario()/load_scenario() use plain save()/load() with no updateObject() step. There is no test fixture guarding this. Several one-off upgrade shims exist (.upgrade_summand() in R/class-constraint.R, .hasSlot() probes in R/class-settings.R and R/class-config.R) — follow that pattern if you must add a slot.
Conventions
- Don’t delete code — move it to
drafts/. - Prefer dplyr/dtplyr and the native pipe (
|>) for data-frame work. - Follow the tidyverse style guide; don’t restyle code unrelated to your change.
- User-facing changes get a bullet at the top of
NEWS.md. - Class and slot documentation is generated from
data-raw/classes.ymlviaget_slot_doc()— keep it in step with thesetClassprototypes when adding or renaming a slot.
Further reading
-
dev-scripts/EXTENDING.md,dev-scripts/PIPELINE.md— the data-driven mapping pipeline -
dev/known_bugs.qmd— open defects -
dev/s7-migration-decision.md— why the package stays on S4 -
.github/CONTRIBUTING.md— PR process, and the policy on AI-assisted contributions
Last verified against 0.71.0.9000 (2026-08-01). Drafted with AI assistance; content is the maintainers’ responsibility. Please re-check the commands and file references at each release — a stale instruction is worse than none.
