Development instructions
Source:AGENTS.md
If you are a human, take these instructions as guidance. If you are an agent, follow these instructions. Ask for clarifications where specifications are unclear or edge cases crop up.
This is an R package that is used for land use / land cover change analyses and simulations. It builds on the approaches of lulcc, clumpy, and Dinamica EGO.
Project layout
-
R/ducklake_db.R,R/ducklake_db_utils.R: generic DuckLake-backed DB layer -
R/evoland_db.R,R/evoland_db_util.R,R/evoland_db_views.R: domain DB and views (inherits ducklake_db); register new methods usingcreate_method_bindinginevoland_dbconstructor -
R/*_t.R: table type definitions (as_*_tconstructors) -
R/util*.R: internal helpers -
R/*.R(remaining): domain logic (allocation, filtering, modelling) -
src/: Rcpp / C++ code -
inst/tinytest/: tests -
inst/dinamica_models/: Dinamica EGO models -
inst/*.sql: SQL queries, interpolated withglue::glue_sql()by$get_query() -
data-raw/: raw data and test fixture generation scripts -
man/: documentation generated by roxygen2 -
vignettes/*.qmd: vignettes and how-tos -
devdocs/: developer documentation; design rationale indevdocs/design/, style guide indevdocs/style/
Environment setup
Use rv. Run rv sync to install all dependencies (see rproject.toml). Recompile and attach the package using pkgload::load_all().
Conventions and Style
Developer documentation is in devdocs/: design rationale in devdocs/design/, the style guide in devdocs/style/. Before writing or editing a file, read the part of the style guide that applies:
| Editing | Read |
|---|---|
R code in R/, tests in inst/tinytest/
|
devdocs/style/r.md |
C++ in src/
|
devdocs/style/cpp.md |
SQL in inst/*.sql or inline in R |
devdocs/style/sql.md |
| Vignettes, roxygen text, Markdown, commits | devdocs/style/prose.md |
The rules that apply everywhere:
- Avoid new dependencies; prefer base R, and avoid the tidyverse (unstable APIs) and niche, seldom-maintained packages.
- Comment why, not what. In R, prefer an unbroken pipe with a short comment over naming intermediate results that are used only once.
- Let errors propagate naturally; only add early guards when the default error message would confuse the user.
- Format what you change:
air format .for R,git clang-formatfor C++, sql-formatter for SQL. - Don’t reformat or rewrite code unrelated to your change.
- Use descriptive git branch names; don’t use container or session names.
Testing
Use tinytest, see inst/tinytest. Non-exported functions need to be tested as evoland:::private_function, because a package can be tested after installation. Be parsimonious when constructing tests. Keep test_<table>_t.R files free of database logic: they test the as_*_t constructors and validation only. Tests that need a database belong in test_db_*.R; extend an instance those files already open rather than creating a new one. Tests that need a longer series of manipulations of the database (fitting models, allocating periods, evaluating runs) belong in test_integ_*.R.
- Test the full package using
R -e "tinytest::build_test_install()". - Test individual files using
R -e "pkgload::load_all(); tinytest::run_test_file('inst/tinytest/somefile.R')"
Documentation
Use markdown roxygen for function and class documentation. Use R -e "roxygen2::roxygenize()" to synchronize roxygen comments and .Rd files. Writte tutorials and how-to as quarto files in vignettes/*.qmd.
Rcpp components
C++ code is in the src/ folder. This code interfaces with R using Rcpp. Build binaries using pkgbuild::build(). Clean build objects using pkgbuild::clean_dll(). Prefer Rcpp types. Only write Rcpp-free headers when the code should also compile as a standalone program.
Database
- Storage is a DuckLake catalog attached to an in-memory DuckDB instance. By default a database is one self-contained folder: SQLite catalog at
<path>/catalog.sqlite, data files under<path>/data/.cataloganddata_pathmove either half elsewhere (PostgreSQL, S3); the extensions needed to open a database are derived from them. -
R/ducklake_db.Rspecifies an R6 class that provides database operations to write and retrievedata.tableobjects. Writes are atomic and readers get snapshot isolation, so several processes may write to one database concurrently; a private retry wrapper absorbs catalog lock contention, which DuckLake’s ownducklake_max_retry_countdoes not cover. The errors it treats as transient are catalog-backend specific – seeTRANSIENT_CATALOG_ERRORS.-
ducklake_db_tis a subclass of thedata.tableS3 class, seeR/ducklake_db_utils.R -
ducklake_db_tobjects can hold attributes used to define- key columns, i.e. uniqueness columns
- hive partitioning columns, which are a pruning hint only
- map columns, i.e. R list columns of named lists translated to DuckDB MAP columns
- Which columns serve which purpose is read from those attributes on the data being committed, and otherwise from the metadata the target table already carries –
ducklake_dbmakes no assumption about how domain constructors are named. - DuckLake supports no constraints, keys or indexes, and
MERGEsilently inserts duplicates from a source with duplicate keys, so$commit()checks uniqueness explicitly before merging. It has no ENUM type either; factors are stored as strings and cast back by theas_*_t()constructors.
-
- Domain specific database elements are in
R/evoland_db.R;evoland_dbinherits fromducklake_db.- The schema for this database is (for now) distributed across the class definitions: all
R/*_t.Rfiles containas_*_tclass constructors usingas_ducklake_db_t. - Because the catalog may be read and written from external tools, it should be considered part of the API. Schema changes should be avoided as much as possible.
- After changing a table’s columns or keys, regenerate the ER diagram with
Rscript devdocs/er_diagram.R; CI fails if it is out of date. - Ad-hoc views are suffixed
_vand generally exposed as active bindings, or as methods if they are parameterized. - Every new method must be added in
R/evoland_db.Rand not with$set. This is because the roxygen documentation routine for R6 objects relies on all documentation being available in a single file.
- The schema for this database is (for now) distributed across the class definitions: all