Skip to content

Latest commit

 

History

History
121 lines (93 loc) · 9.71 KB

File metadata and controls

121 lines (93 loc) · 9.71 KB

AGENTS.md — bycatch

R package bycatch (v0.10.4) — seabird bycatch risk assessment. Maintainer: IslasGECI.

Commands

Command What it does
make tests Run all tests (devtools::test)
make tests_fast Run fast tests only (skips tests/testthat/slow/)
make tests_slow Run slow tests in tests/testthat/slow/ only
make tests_file file=<path> Run a single test file (no commit/restore)
make format Auto-format R/ and tests/ with styler
make check Check formatting only (no auto-fix)
make clean Remove *.tar.gz, tests/testthat/_snaps, NAMESPACE
make setup make cleandevtools::installcheckbuilddocument
make coverage Run tests + generate HTML coverage report
make red/green/refactor TDD phase targets with auto-commit/restore

devtools is only available inside Docker. Run tests via docker exec bycatch_code_ci make tests. Each test file contains exactly one describe() block. make tests_file does not work for files in slow/; use testthat::test_file() directly.

Fast suite (~52s, 46 tests): 21 files. Slow suite (~6.5min, 2 tests): 2 files. Bottleneck: slow/test_create_potential_kba.R (166s) and slow/test_compute_potential_kba.R (222s), both call track2KBA::findSite.

TDD commits use emoji convention: 🛑🧪 for Red (failing tests), ✅🧪 for Green (passing implementation), ♻️ for refactor, 📝 for documentation.

Testing

devtools::test() loads code from R/ source directory — no rebuild needed. Only run make install to regenerate NAMESPACE, docs, or check CMD warnings.

TDD workflow

  • Red phase: format tests, run. If they fail → stage tests/testthat/*.R → commit with 🛑🧪. If pass → git restore .
  • Green phase: format R/, run. If pass → stage R/*.R → commit with ✅🧪. If fail → git restore .
  • Refactor phase: format both, run. If pass → stage all → commit with 📝. If fail → git restore .
  • Single-file variants: make red_file, green_file, refactor_file file=<path>

Architecture

Three levels: Level 1 Pure (compute/plot, no I/O), Level 1 I/O (read/write/import/export, no computation), Level 2 Artifact (create/render, orchestrate). Docs: Arquitectura, Guía de estilo, Desacoplamiento.

Naming convention

Prefix Level Role
compute_* 1 Pure In-memory calculation, no I/O
plot_* 1 Pure In-memory ggplot2, no I/O
.name (dot) 2 Helper Private, called only by Level 2
create_* 2 Artifact Read → compute → write
render_* 2 Artifact Read → plot → write
import_* / export_* 1 I/O Disk I/O only, no computation

Layer rules

  • Level 1 Pure never reads/writes files. Level 1 I/O only reads/writes files, never computes.
  • Level 2 composes Level 1 functions. Only Make orchestrates Level 2 — they never call each other.
  • Every track2KBA function appears in exactly one compute_* (or plot_*).
  • Every compute_* appears in exactly one create_* (or render_*). No compute_* calls another — read intermediate results from disk instead.
  • Row filters ([, dplyr::filter) belong in compute_* functions, never in create_*/render_*. Exception: row filtering in import_* is permitted I/O adaptation.

Purity

Level 2 functions accept a single options list via get_domain_specific_options(). Every option key read by Level 2 must have a corresponding flag definition in R/get_domain_specific_options.R and be listed in test_get_domain_specific_options.R — otherwise CLI calls fail with "long flag is invalid".

compute_* functions are pure (return lists or data.frames). plot_* functions return ggplot2 objects. Disk I/O lives only in create_*/render_* functions.

Package structure

  • R/ — 5 files: cli.R (Level 2: create_, render_), compute.R (all compute_), plot.R (internal plot_), io.R (import_config, import_trips, import_trips_summary), get_domain_specific_options.R (parser flags).
  • tests/testthat/ — 21 fast + 2 slow in slow/. One describe() per file. Paths hardcoded as /workdir/tests/data/… (Docker convention).
  • tests/data/ — CSV and RDS fixtures generated by tests/src/create_test_fixtures.R.
  • track2kba/ — Read-only reference clone of BirdLifeInternational/track2kba (clean checkout, not a submodule).

Testing quirks

  • Hash fragility: MD5 hashes of RDS output files (tools::md5sum) are fragile — they break on R serialization format changes, package version bumps, and numerical drift. For regression tests that guard against a specific algorithmic change, prefer comparing a single numeric value from the output against a threshold at the midpoint of the two versions:

    1. Run both versions (old and fixed) to obtain V1 and V2.
    2. Set threshold at midpoint (V1+V2)/2, rounded to nearest 1,000.
    3. Assert the output is on the correct side of the threshold. Example: sum(UDPolygons$area) or max(UDPolygons$area) discriminate between two smoothing parameters (mag vs scaleARS) because they produce materially different polygon areas (~44% difference). The midpoint provides a ~15% safety margin against numerical drift.
  • covr::package_coverage() runs only the fast suite — render_* bodies show as uncovered (exercised only by slow tests).

  • Coverage script: tests/testthat/coverage.R. HTML report → tests/coverage-report.html.

  • Fixture regeneration: docker exec bycatch_code_ci Rscript tests/src/create_test_fixtures.R.

  • Fixture scripts access internal compute_* via bycatch::: (they are never exported).

CI

GitHub Actions: docker buildmake checkmake coveragemake mutants → push images to Docker Hub.

Commit conventions

Each commit: Gitmoji + imperative verb + under-72-char summary. Blank line. Body explains motivation (why, not what). No Conventional Commits prefixes (feat:, fix:).

Repo invariants

  • styler formatting is mandatory. make check enforces it in CI.
  • roxygen2 with markdown = TRUE. R/*.R tags are source of truth; NAMESPACE and man/ are build artifacts removed by make clean and regenerated by devtools::document(). They are intentionally not committed.
  • Bug-scope investigation: cross-reference ALL Level 2 functions against get_domain_specific_options() — missing flags often affect more functions than reported. Use grep('options\\[\\[', R/cli.R) to find all consumed keys.
  • Cache design: individual_kde.rds (KDE_surface, UDPolygons, tracks) and assessment.rds (assessment_summary, assessment_detail, KDE_surface).
  • Colony is kept only in compute_* calls to track2KBA algorithms (tripSplit, tripSummary). plot_* and render_* never receive or use colony.
  • sf_use_s2(FALSE) save/restore lives in every compute_* and plot_* function that directly calls track2KBA (estSpaceUse, repAssess, findSite, mapSite, mapKDE). Uses save/restore pattern: s2_was_true <- sf::sf_use_s2(FALSE); on.exit(sf::sf_use_s2(s2_was_true)). Not needed in create_*/render_* — they delegate to the leaf functions. No test file should call sf_use_s2(FALSE) externally.
  • --smoothing-method (-z) was removed in v0.10.4. create_individual_kde now always uses scaleARS (Area-Restricted Search scale from First Passage Time analysis) via compute_scale_parameterstrack2KBA::findScale.
  • create_processed_data was removed in v0.10.0. Its role (composing compute_individual_kde + compute_representative_assessment) is now embedded in create_representative_assessment.
  • Geometry validity contract: compute_potential_kba applies sf::st_make_valid to the output of track2KBA::findSite before returning. This guarantees that KBA polygons written to GeoPackage by create_potential_kba are valid, preventing TopologyException crashes in render_potential_kbatrack2KBA::mapSitest_union. If the fix is needed elsewhere, it should live in a compute_* function (computation, not visualization).
  • CHANGELOG strategy: Document only exported (Level 2) functions (create_*, render_*, get_domain_specific_options). Level 1 internal functions (compute_*, import_*, plot_*) are implementation details and excluded. This keeps the user-facing API changelog clean and separates public contract from internal refactoring.
  • Cross-repo validation: When updating CLI signatures or exported functions, audit all downstream consumers (e.g., ../bycatch_thesis/Makefile) — verify every bycatch:: call references an @export function. Stale function calls can silently break dependency workflows.
  • Documentation sync: After renaming or changing signatures, verify all references in DOCS.md, README.md, roxygen @param tags, and test files. Stale docs in roxygen tags cause CLI failures ("long flag is invalid").
  • plot_*() delegation pattern: plot_potential_kba() and plot_individual_kde() delegate to track2KBA::mapSite() and track2KBA::mapKDE() respectively (no custom ggplot2 code). plot_representative_assessment() clones track2KBA's repAssess() plotting logic (confidence band formula: meanPred ± est_asym × sdInclude). Internal functions; changes require test fixture updates but do NOT appear in CHANGELOG (per documentation strategy).
  • Cache structure expectations: render_representative_assessment() expects cache RDS with BOTH $assessment_detail AND $assessment_summary (including est_asym field). Old caches without est_asym will cause failures.
  • License: AGPL-3.0-or-later.