This document defines the shared expectations and tooling standards for all R packages in the Artalytics platform. It serves as a common reference for agentic tools (e.g., GitHub Copilot Agents, Sourcegraph, Cursor) to interact with each repository consistently and with full context. It covers both the internal utility libraries (like artcore, artutils, artopenai, etc.) and the Shiny module packages (like modBrowse, modUpload, modGallery, etc.) that together make up the Artalytics ecosystem.
Project Purpose and Ecosystem Overview
The Artalytics platform is composed of multiple R packages, each serving a specific role but adhering to common standards:
Core Utility Libraries (Internal): Packages such as
artcoreandartutilsprovide foundational functions and shared utilities for the platform. These are not directly user-facing but are consumed by other packages.artcorecontains the lowest-level platform functions (database connections, CDN interfaces), whileartutilsbuilds onartcoreto offer higher-level tools and data access functions used across modules and apps.API Integration Libraries (Internal): Additional internal packages like
artopenai(OpenAI integration),artgemini(Gemini integration),artsend(Resend email API),artopensea(OpenSea API), orartpixeltrace(artwork verification) extend functionality. They follow the same standards, ensuring they integrate seamlessly with core utilities.UI Module Packages (Shiny-facing): Packages such as
modArtist,modBrowse,modUpload, andmodGalleryimplement Shiny modules (UI components + server logic) for the platform’s web interface. These modules are designed to be plugged into the main Artalytics Shiny application housed inappPlatform. They are developed as independent packages for modularity, but share the platform’s coding conventions and infrastructure.Platform Application: The
appPlatformpackage serves as the main parent application that orchestrates and integrates allmod*module packages into a cohesive user experience.
All these packages are versioned, documented, and tested consistently. These standards govern the full ecosystem, ensuring that whether a package is backend-focused or UI-facing, it conforms to the same high-level guidelines.
Package Dependency Hierarchy
The Artalytics platform follows a layered architecture with clear dependency boundaries:
┌─────────────────────────────────────────────────────────┐
│ appPlatform │
│ (Main Shiny Application) │
├─────────────────────────────────────────────────────────┤
│ modArtist │ modBrowse │ modUpload │ modGallery │
│ (Shiny Modules) │
├─────────────────────────────────────────────────────────┤
│ artopenai │ artgemini │ artpixeltrace │ artcurator │
│ (API & Domain Packages) │
├─────────────────────────────────────────────────────────┤
│ artutils │
│ (High-level utilities & data access) │
├─────────────────────────────────────────────────────────┤
│ artcore │
│ (Low-level: DB connections, CDN interfaces) │
└─────────────────────────────────────────────────────────┘
Key principles:
-
artcoreprovides the only functions allowed to open/close database connections (artcore::..dbcandartcore::..dbd) and the lowest-level CDN interface functions. -
artutilsbuilds onartcoreto provide higher-level database query functions and CDN utilities that apps and modules rely on. -
Modules and apps should ideally access data through
artutilsfunctions rather than callingartcoredirectly. The goal is to abstractartcoreaway from higher-level packages.
Note: During active development, these boundaries may be relaxed. For example,
artcuratorcurrently makes its own database queries. The long-term objective may involve consolidating all query logic into a dedicated package for true separation of concerns. This architecture is evolving.
Strict Dependency Rules
To maintain a clean, acyclic dependency graph, the following rules must be observed:
| Package Layer | Allowed Internal Dependencies |
|---|---|
artcore |
None – must have zero Artalytics package dependencies |
artutils |
artcore only |
API/Domain packages (artopenai, artpixeltrace, etc.) |
artcore and/or artutils
|
Shiny modules (mod*) |
artutils (and by extension artcore), but never other modules
|
appPlatform |
All module packages and utilities as needed |
Critical constraints:
artcoreis dependency-free (internally): It should not import any other Artalytics-specific packages. It can rely on CRAN packages or third-party libraries, but it stands alone in the Artalytics ecosystem. This ensuresartcoreremains lightweight and universally usable.artutilsdepends only onartcore: Among internal packages,artutilsmay only importartcore. External CRAN dependencies are permitted.Modules must not import each other: Shiny modules are designed to be independent. If modules need to communicate or share state, that coordination happens via
appPlatform’s main app logic—not by one module package importing another. This prevents circular dependencies and keeps modules independently testable.
Deployment Scope
This section clarifies how each type of package fits into deployment and the overall dependency structure.
Module Packages Are Not Standalone
Packages like modBrowse, modUpload, modGallery (and similar modules) are not standalone applications—they are components designed for integration into the main Artalytics Shiny app. Key implications:
-
Platform context assumed: Modules expect the presence of platform infrastructure (e.g., global options,
artutilsfunctions, shared reactive values). They will not function correctly if run outside ofappPlatform. -
No direct deployment: Modules are installed as dependencies of
appPlatform, not deployed independently. -
Development testing: Each module ships with an
app.Rfile that wraps the module in a minimal parent structure for isolated development and testing. This is for development only—not production deployment.
Version Management
-
Version pinning: The main app’s (
appPlatform) DESCRIPTION file pins compatible versions of module packages. When deploying, ensure module versions match the main app’s expectations. -
Coordinated updates: If
artcoreis updated, dependent packages (artutils, modules, etc.) should be updated in tandem to remain compatible. Incompatible changes must be avoided or coordinated across the ecosystem.
Inter-Module Communication
Modules are designed to be independent and must not import each other. When modules need to communicate or share state:
- Communication happens through
appPlatform’s server logic - Shared data is passed via reactive values managed by the parent app
- Global state or configuration is provided by
artutilsor app-level context
This design ensures modules remain independently testable and avoids circular or tangled dependencies.
Directory Structure
All Artalytics R packages use the same structure, with some modifications for Shiny app/module packages and API integration packages. Key aspects of the directory structure include:
-
R/– Core R source code (functions, module definitions, etc.). -
man/– Documentation files (.Rdhelp files) generated by Roxygen2. -
tests/– Unit tests (testthat) intests/testthat/subdirectory. -
inst/– Additional files to install (e.g.,inst/extdatafor example data). -
vignettes/– Long-form docs/guides (often written in R Markdown or Quarto). -
.github/workflows/– CI workflow YAML files for GitHub Actions.
Shiny Module Packages:
In Shiny module packages, you will find additional components:
-
app.R– A standalone app file shipped with each module package for development and iteration. This app wraps the module in a minimal parent structure, allowing developers to test the module in isolation. -
inst/www/– Static assets for the module’s UI (images, JS, CSS) that are deployed with the package. - Quarto documents may appear in
vignettes/demonstrating module usage.
Database and CDN Access Patterns
Database Connections
Database connectivity follows a strict pattern:
-
Connection functions: Only
artcore::..dbc()(connect) andartcore::..dbd()(disconnect) are permitted for database connections. -
Query execution: Historically, only
artutilsfunctions were allowed to send queries and receive data. This rule has been relaxed during development (e.g.,artcuratormakes its own queries). -
Session pattern: Shiny modules and apps typically make a database connection at session start to retrieve all data needed for the session. Exceptions include
modUpload, which performs write/modify/delete operations based on user actions.
CDN Access
CDN (DigitalOcean Spaces) connectivity follows a similar layered pattern:
-
Low-level interface:
artcorecontains the CDN connection and interface functions. -
High-level utilities:
artutilsprovides higher-level CDN functionality that apps and modules use. -
Objective: Apps and modules should use
artutilsCDN functions rather than callingartcoredirectly.
Documentation & Vignettes
All Artalytics packages share the same build process and documentation standards to ensure uniformity:
Documentation: Documentation is written as Roxygen2 comments for function files in
R/*. These are generated into.Rdfiles inman/*usingdevtools::document(). All exported functions and user-facing objects must have Roxygen2 doc comments.Vignettes: Packages can include vignettes (in
vignettes/), written in R Markdown or Quarto, to provide tutorials or extensive examples. Callusethis::use_vignette("vignette_name")for setup. Usedevtools::build_vignettes()to build them. Vignettes should be included in the package build so that users can access them viavignette("vignette_name").R CMD check: Every package must pass
R CMD checkwithout errors or warnings. All examples, tests, and documentation are run/tested during the build. Developers are expected to run R CMD check (usedevtools::build/devtools::checkfrom R) locally before pushing changes. This ensures that all packages remain production-quality in terms of documentation and build integrity.
By adhering to these build and documentation standards, we guarantee that all packages have consistent, high-quality documentation and are built reproducibly. Agent tools can rely on Roxygen comments to provide in-context help, and contributors can expect a similar development experience across packages.
Testing Standards
All packages follow these testing practices:
Unit Testing (testthat): Use
usethis::use_test()to setup testthat anddevtools::test()(or testthat equivalents such astest_local).Code Coverage (
covr): Use thecovrpackage to measure test coverage. Aim for high coverage (80%+) on critical functions. Coverage reports are generated locally withcovr::package_coverage.Tabular Datasets in R: Always use
data.tableas it’s highly efficient and its compact syntax is greatly preferred over data.frames, tibbles, and dplyr syntax.data.framesare strictly avoided.-
Snapshot and UI Testing: For Shiny app and module packages, we utilize snapshotting features throughout the suite of testing packages:
-
Shiny modules: Use
shinytest2framework to simulate a Shiny session and verify module behavior (inputs lead to correct outputs or UI state). -
API packages: Use
mockeryorhttptest2for mocking external API calls. -
General snapshots: Use
testthat::expect_snapshot()to capture function output for stability across changes.
-
Shiny modules: Use
Testing Scope
-
Module packages (
mod*): Tests should focus on the module in isolation. Each module package ships with anapp.Rfile that wraps the module for development testing. -
Integration tests: Cross-module and full application integration tests belong in
appPlatform, not in individual module packages. - Exported functions only: Only exported package functions should have corresponding unit tests. Internal functions are typically covered by testing their parent function callers. This keeps test code compact, manageable, and robust.
Style Guide and Coding Conventions
We maintain a consistent coding style across all R packages to make the codebase easy to read and navigate. Key style guidelines include:
Favor
data.tableover tidyverse: We prefer usingdata.tablefor data manipulation rather thandplyror other tidyverse packages. This means using data.table’s syntax (DT[i, j, by]and chaining with[]) for subsetting and aggregation. The codebase avoids attaching the tidyverse; instead it leans on base R and data.table for performance and clarity. This also reduces external dependencies.Native Pipe Operator: All packages that utilize the pipe operator must use R’s native pipe
|>(introduced in R 4.1+).String Handling: We prefer stringr for string operations (excluding concatenation, see next) over base R string functions. For example, use
stringr::str_detect,str_replace, etc., instead of base R’sgrepl,gsub, etc. This provides more readable and robust regex handling. All string constants should be written clearly, and where patterns are complex, commented for clarity.Concatenation: Use
paste0()instead ofpaste()when concatenating strings without a separator. In general, be mindful of unnecessary whitespace or separators in output. Consistency in constructing strings (especially for file paths or messages) makes the behavior predictable.-
Code Conciseness: Aim to write code that is compact. Implementations that meet all requirements while reducing lines of code are always preferred (all else equal). Shiny app code has the reputation of quickly becoming too large and challenging to manage as features are added and tech debt grows.
For example: data.table is strictly favored for its efficiency, and it comes with the added benefit of having highly compact syntax. When used in conjunction with a naming convention that prioritizes more comments but fewer character names, we can produce an elegant codebase that can be frequently de-risked of tech debt—ensuring quality standards are always adhered to.
Function Documentation and Export: All exported functions (and any internal ones that are complex) should have roxygen2 documentation blocks. This includes description, parameter documentation, return value, examples, and any
@seealsoor@referencesif relevant. This allows tools and contributors to quickly grasp usage. Internal (non-exported) functions can have shorter comments if needed, but key ones often get a@noRdroxygen comment for clarity in the code.Linting: We use lintr in CI to enforce a basic style check on all packages. Developers should strive to write code that passes
lintr::lint_package()while ensuring specific organization-adopted styling is maintained. Note that our style leans toward base R and data.table usage, which is compatible with the default lintr settings.Naming Conventions: Use
snake_casefor function and variable names (consistent with base R and tidyverse conventions). Filenames inR/generally match the name of the primary function or concept defined in the file (to ease navigation).
CI/CD and GitHub Actions
Continuous Integration (CI) and Continuous Deployment/Development workflows are set up for each package to maintain quality and consistency:
-
GitHub Actions Workflows: Each repository contains YAML files under
.github/workflows/that automate build and test processes. At minimum, there is a workflow for runningR CMD check(which includes tests) and a workflow for linting. Many packages also have a test coverage workflow and possibly a deploy or pkgdown documentation workflow.- The R CMD check workflow installs the package’s dependencies (including other Artalytics packages from GitHub if listed in Remotes) and runs
R CMD checkon the package across one or more OS (commonly Ubuntu, and sometimes Windows/macOS as needed). It uses the standard r-lib/actions for consistency. - The Lint workflow runs
lintr::lint_package()to ensure coding standards are met (without failing the build on style issues as of now). - The Coverage workflow (if present) uses covr to run tests and upload coverage results to a service like Codecov.
- The R CMD check workflow installs the package’s dependencies (including other Artalytics packages from GitHub if listed in Remotes) and runs
Quality Gates: A pull request must pass
R CMD check(which implies all tests passed) and linting before it can be merged. This gate keeps the main branch stable and clean.-
Secrets & Private Dependencies: Many Artalytics packages depend on other internal packages (not on CRAN). To allow CI to install these, the GitHub Org secret
GITHUB_PATis injected into the workflow environment. Setting this during the build process letspak::pkg_install("artalytics/{repo_name}")install the private package. Similarly, any API keys or tokens needed for tests are provided via secrets:- For example, test coverage upload uses a
CODECOV_TOKENsecret (set in the repo settings) so thatcovr::codecov()can report coverage. - If a package needed an API key (say for OpenAI), it would be set as an env var (e.g.,
ART_OPENAI_KEY) in the workflow, rather than hardcoded.
- For example, test coverage upload uses a
All CI/CD workflows are kept similar across repos, which means an agent or developer can look at the .github/workflows/ folder and navigate them easily. If you’re in one package, you can expect the same checks and balances to apply in another, fostering a unified development workflow.
Security and Secret Handling
Security best practices are critical in our development process. All packages follow these rules regarding secrets and sensitive information:
Env Vars for Secrets: Store secrets in
.Renvironfor development/iteration and never commit this file. In production or CI, these are set through secure means (never hardcoded).CI Secret Injection: In CI workflows, any required secrets (PATs, tokens) are added via GitHub Actions secrets. As noted, the
GITHUB_PATfor installing private packages (pak::pkg_install("{org}/{repo}")).Runtime Config: Artalytics platform packages and apps utilize certain standard environment variables at runtime across packages.
Environment Variables Reference
The following environment variables are used across the platform:
Application Mode
| Variable | Description | Example Value |
|---|---|---|
ART_RUNAS_DEMO |
Disables all UI actions that write/delete/modify data across any source (database, CDN, etc.). Used for deploying demo applications. This will be phased out as authentication/login is rolled out. | TRUE |
Database Configuration
Used in artcore for creating connections managed by artutils query functions:
| Variable | Description | Example Value |
|---|---|---|
ART_USE_CONFIG |
Toggle database context | artdev |
ART_PGHOST_DEV |
Managed DB host | art-dev-do-user-....ondigitalocean.com |
ART_PGPORT_DEV |
Database port | 25060 |
ART_PGUSER_DEV |
Dev database user | shiny |
ART_PGPASS_DEV |
Dev user password | (set in .Renviron) |
Appendix
Repository Reference
Most Artalytics repos are private; programmatic access requires your GITHUB_PAT. Public entries are marked “public” and don’t need the token.
| Repository | Description | Visibility |
|---|---|---|
| modFrames | Artalytics Platform Shiny Module for Artwork-Creation (Replay) Analytics. | private |
| artutils | Package containing tools and data shared across the modules of the Artalytics Shiny app | private |
| appPlatform | Artalytics Platform Main App that Wraps All Shiny Modules into a Single Parent Application. | private |
| artpipelines | Pipelines that generate data needed by the Artalytics platform | private |
| modGallery | Artalytics Platform Shiny Module Containing Digital Gallery Experiences for Authenticated Artwork Projects. | private |
| modUpload | Artalytics Platform Shiny Module for Uploading/Managing Artwork Projects. | private |
| modBrowse | Artalytics Platform Shiny Module for Browsing Artworks and Collections. | private |
| artbenchmark | Platform Tools for Robust Artwork Valuation and Performance/Price Benchmarking of Authenticated Works. | private |
| artpixeltrace | Artwork verification and certificate generation tools used by the Artalytics Platform. | private |
| artopensea | Tools for an OpenSea integration used by the Artalytics App Platform. | private |
| artopenai | Tools for an OpenAI integration used by the Artalytics App Platform. | private |
| artcore | Core Tools Required Across Platform R Packages, Shiny Modules, and Deployed Applications. | private |
| modArtist | Artist Profile is a module of the artalytics platform and serves as the entry point to the app. Provides complete user gallery experience: Learn about Artist → Browse their collections → Explore an artwork → Dive deeper with replay analytics → Explore the Artist’s stores | private |
| artsend | API integration package enabling resend functionality throughout the platform. | private |
| artauth | Authentication and access control for Artalytics platform - passwordless magic links, user account management, role-based access (artists, collectors, investors), and lead capture functionality | private |
| arthelpers | Internal development-only toolkit to address codebase/data management, architecture audits, debugging/feature development utils. | private |
| artshiny | Shared Shiny UI components for Artalytics platform | private |
| artcurator | Package containing tools for retrieval of data, memory, and database plus s3 bucket context, specifically to enable an integrated layer for CuratorAI - an AI chatbot living inside the Artalytics web app that understands an artist, an artwork, the metadata, the provenance, valuations, timelines, collection context, etc | private |
| artgemini | R package containing API client functions and tools related to Google’s Gemini AI Platform API. | private |
TODO Summary
The following areas require additional documentation:
-
artshinypackage - Document shared UI components and utilities - Database query separation - Finalize and document target architecture
- CDN access patterns - Document artcore/artutils CDN functions and usage patterns
- Testing examples - Add reproducible examples for shinytest2, httptest2, snapshots
- Style guide examples - Add concrete code examples for preferred patterns
- Environment variable helper - Document internal pattern for retrieving/validating env vars
