Imported from traitecoevo/APD (
AGENTS.md). Install upstream withnpx skills add traitecoevo/APD. Copyright stays with the author.
APD — agent & contributor guide
APD is a Quarto compendium that builds the AusTraits Plant Dictionary — a formal vocabulary of
500+ plant traits, released simultaneously in human-readable and machine-readable formats (Wenk et
al. 2024, doi:10.1038/s41597-024-03368-z).
Repo-local guidance
-
⚠️ Read
COMMITMENTS.mdfirst if you are about to change a URI scheme, an output format, the licence, the set of published input tables, or where the site deploys. The APD is described in a published paper (Wenk et al. 2024), and that paper is a specification — several of its claims are promises this repo has to keep, and some are currently broken. -
Editing a trait?
CONTRIBUTING.mdhas the spreadsheet and YAML routes, and what counts as a breaking change. Cutting a release?RELEASING.md, whose last three steps are outside this repo and are the ones that get missed. -
Source data:
data/holds the inputs that define the dictionary. Trait definitions live inAPD_traits_input.yml, which is the source of truth — YAML was adopted in #43 because CSV diffs were unreviewable.make export-csvchecks out a spreadsheet view atdata/edit/APD_traits_input.csv(gitignored) andmake import-csvwrites it back; the round trip is byte-lossless and tested. Every scalar in the YAML is quoted text, includingmin/max— they end up in RDF literals, so text is what they are, and storing them as doubles made the published values depend onoptions(scipen).The remaining inputs are CSVs: allowable categorical values (
APD_categorical_values_input.csv), the trait hierarchy, glossary, units, references, reviewers, namespaces, and annotation properties.APD_namespace_declaration.csvis the only namespace map the build reads — don't add a second one inR/. -
Build: a
Makefileat the root drives small scripts inscripts/, which call the functions inR/.make databuilds the APD fromdata/intoexport/— RDF Turtle (APD.ttl), N-Quad (APD.nq), N-Triple (APD.nt), JSON-LD (APD.json), plus the derivedAPD_traits.csvandAPD_categorical_values.csv. Nothing in the build writes todata/, andmake checkenforces that. -
Published URLs are the contract, not repo paths.
export/artefacts are copied to the site root byscripts/build_site.R, sohttps://traitecoevo.github.io/APD/APD.ttland the w3id rules pointing at it never move. Don't list them as quarto resources — a resource keeps its relative path, which would publish them underexport/and break every existing link. See COMMITMENTS.md C12. -
Website: a Quarto website (
_quarto.yml,index.qmd,using_the_APD.qmd,NEWS.md) rendered todocs/and published via GitHub Pages at https://traitecoevo.github.io/APD/. The dictionary is also resolvable via https://w3id.org/APD/.Never add a
<link href=...>viainclude-in-header. Every page setsembed-resources: true, and that pass inlines any<link>target it can resolve. Adding arel="canonical"link that way made quarto fetch the live site and embed all 6 MB of it as adata:URI — the page went from 6.1 MB to 15.2 MB and thehrefwas replaced by the inlined document. Anything that has to reach the<head>of these pages gets added after the render, inscripts/build_site.R.docs/is gitignored — a local build artefact likeexport/.deploy.ymlrenders it fresh onmaster; nothing reads the committed tree, because there isn't one. It also carriesrelease/, which is not a quarto resource any more, so a localmake siteyields adocs/without the versioned snapshots. That is expected. -
R helpers:
R/holds supporting functions.DESCRIPTIONis the one declaration of what the build needs —scripts/setup.Rattaches the subset the unqualified dplyr/tidyr/gt/readr verbs inR/require, and CI installs fromDESCRIPTION, so don't keep a second list anywhere. Attach the packages you use, nottidyverse— the umbrella hides which six of its packages are load-bearing and pulls in ggplot2, lubridate and forcats, which nothing here touches.jsonldis the one entry that looks unused:rdflibcalls it to writeAPD.jsonand only suggests it, so it has to be declared here or a fresh checkout cannot produce that output at all.
Run make for the list of targets:
| Target | Does |
|---|---|
make data |
validate inputs → triples → RDF + the two flat CSVs |
make check |
validation report + tests |
make site |
data, then render the website into docs/ (slow, ~75 s; offline) |
make release |
check + site + version checks + snapshot into release/<version>/ |
make export-csv |
trait YAML → CSV, for spreadsheet editing |
make import-csv |
CSV → trait YAML, printing the per-trait diff |
make clean |
delete export/ and the Quarto cache |
Rscript scripts/sparql_examples.R runs example SPARQL queries against APD.nq.
This is a Compendium/Bundle, not an R package — there is no devtools::check() workflow.
Continuous integration
Four workflows in .github/workflows/, plus the issue-triage one:
| Workflow | Trigger | Does |
|---|---|---|
check.yml |
push to master/develop, every PR |
make data, assert the build wrote nothing to data/, make check |
render.yml |
PR touching data/, R/, scripts/, assets/, a .qmd or _quarto.yml |
make site, and uploads the rendered page as an artefact |
deploy.yml |
push to master |
renders, publishes to Pages, then verifies the live site |
redirects.yml |
Mondays, and on demand | scripts/check_redirects.sh against the live service |
scripts/check_redirects.sh is the one check that tests something this repo does not contain: the
w3id.org rules live in perma-id/w3id.org
and can drift away from this site without a commit here. It reports known gaps without failing, in the
same three severities make check uses, and fails if a known gap starts passing — a register entry
that outlives its problem silences a check.
Branches and releases
develop is the default branch and where all work lands. master is the published release — its
tip is always exactly the last released state, and it moves only when a release is cut.
| Merge | How | Why |
|---|---|---|
feature branch → develop |
squash | One commit per PR. |
develop → master |
fast-forward, at a release | Keeps master an ancestor of develop, so ancestry still answers "which release shipped this change". |
git checkout master && git merge --ff-only develop && git push
If that refuses, the branches have diverged and the reason needs finding, not forcing.
master used to move on every PR, and that was Pages' fault, not the merge strategy's. Pages
served master:/docs, so a doc fix or a site tweak had to reach master to go live — which is why
commits like #44 and #46 sit on a branch meant to be a release line. deploy.yml publishes via
Actions now, so that constraint is gone and master can sit still between releases. Consequences
worth knowing:
- The live site changes only at a release.
https://w3id.org/APD/serves the latest release, not the latest commit — which is what it should mean for a citable vocabulary. If a fix needs to be live, cut a patch release; 2.1.1 was exactly that. - C13 starts meaning what it says.
austraits.buildreadsraw.githubusercontent.com/traitecoevo/APD/master/data/APD_trait_hierarchy.csv. That used to resolve to the Pages branch; now it resolves to the last release. It is the last surviving instance of the bug C12 already fixed for every other artefact — see COMMITMENTS.md.
Do not squash develop into master to get a one-line-per-release log. It would create a commit
on master that is not in develop, permanently diverging the two: git log master..develop would
stop meaning "work not yet released", later merges would stop being fast-forwards, and a release tag
on master would point at a commit no other branch contains — which matters for a repo whose value
proposition is persistent, citable identifiers, and whose tags Zenodo archives. Release history is
git tag and the Releases page; that is where to read it, not git log master.
Version bumps. DESCRIPTION is the single source (R/version.R); index.qmd and
scripts/release.R read it. Bump it when the published output changes, not only when a trait does —
2.1.1 was a patch release with no change to any definition, because the RDF began asserting typed
numbers where it had asserted strings. make release refuses to overwrite an existing
release/<version>/, so the snapshot for a shipped version cannot be rewritten by accident.
Heads-up: everything in
export/is generated bymake data— edit the inputs indata/, then rebuild; don't hand-edit them.docs/is likewise built output — and gitignored, so if you find yourself staging it, something has gone wrong.
AusTraits family — cross-package context
APD is part of the AusTraits family (a subset of the
traitecoevo org) — here, the AusTraits Plant Dictionary — the
trait vocabulary/contract (definitions, allowed categorical values, units). Family-wide concerns are
documented centrally in
austraits-meta — don't restate them here, read
them there:
- Start with
AGENTS.md— pipeline order, who owns what, dependency direction, source-of-truth rules, cross-boundary artifacts, gotchas. dependencies.yml— machine-readable package graph + cross-boundary artifacts.governance/— label taxonomy, board #9 conventions, release playbooks, triage.
Filing issues: the whole family is tracked on one board,
AusTraits #9 (new issues auto-add to it). Follow
the issue & labelling guide:
pick one work-type label (bug / task / epic); Status and Priority are set on the board, not as
labels.
Commit messages: every family repo squash-merges, so the PR title and body become the permanent
commit message. Keep the subject ≤50 characters as typed and the body ≤10 lines; put the working
detail — what you tried, benchmarks, test counts, rejected alternatives — in the first PR comment
instead. Full convention:
commit-messages.md.
austraits-meta is hand-maintained prose — a map, not ground truth. Verify specifics against the actual repos.