From 790f6f98890abe4f095c9bc0e3fcfc402ff6dab9 Mon Sep 17 00:00:00 2001 From: osenan Date: Thu, 24 Sep 2026 13:50:36 +0200 Subject: [PATCH 01/20] chore: add first version of CLAUDE.md --- CLAUDE.Rmd | 18 ++ CLAUDE.md | 532 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 550 insertions(+) create mode 100644 CLAUDE.Rmd create mode 100644 CLAUDE.md diff --git a/CLAUDE.Rmd b/CLAUDE.Rmd new file mode 100644 index 00000000..b1becc63 --- /dev/null +++ b/CLAUDE.Rmd @@ -0,0 +1,18 @@ +--- +title: "teal.code R Package Development Guide" +output: github_document +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) +``` + + + +```{r teal_content, echo=FALSE, results='asis'} +common_claude_url <- "https://raw.githubusercontent.com/insightsengineering/teal/main/AGENTS.md" +common_claude_path <- tempfile() +download.file(url = common_claude_url, destfile = common_claude_path, quiet = TRUE) +common_claude_shared <- readLines(common_claude_path) +cat(common_claude_shared, sep = "\n") +``` \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..ff938f1f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,532 @@ +teal.code R Package Development Guide +================ + + + +# Teal Ecosystem R Package Development Guide + +## Introduction + +The teal framework uses shiny to create reproducible environments for +analysts. The ecosystem comprises several interconnected packages with +specific roles: + +### Core Packages + +- **teal** - The main framework package providing the application + structure +- **teal.code** - Bare code generation and evaluation ensuring + reproducibility +- **teal.data** - Data management and relationships between datasets + (contains sample data for ADaM datasets and default keys to merge ADaM + datasets) +- **teal.reporter** - Report generation functionality +- **teal.slice** - Data filtering capabilities for application +- **teal.widgets** - Reusable UI components +- **teal.logger** - Standardized logging across the framework +- **teal.picks** - Data selection and merging utilities using + `teal.data` objects +- **teal.transform** - Data transformation utilities (deprecated in + favor of teal.picks) + +### Module Packages + +- **teal.modules.general** (tmg) - General-purpose analysis modules +- **teal.modules.clinical** (tmc) - Clinical trial specific modules +- **teal.modules.hermes** - MultiAssayExperiment analysis modules +- **teal.goshawk** - Pharmacokinetics analysis modules +- **teal.osprey** - Advanced clinical analysis modules + +### Supporting Packages + +- **tern** - Statistical analysis functions +- **rtables** - Table creation and formatting +- **formatters** - Output formatting utilities +- **gtsummary** - Table creation and formatting + +**Key Principle**: Balance dependency value with features. Minimize +dependencies to packages not already in use within the ecosystem. + +## Package Structure and Organization + +### Standard Package Layout + +Follow the standard R package structure with teal-specific conventions: + +``` text +package_name/ +├── .github/workflows/ # CI/CD workflows (use r.pkg.template) +├── R/ # R source code +├── tests/testthat/ # Unit tests +├── man/ # Documentation +├── vignettes/ # Long-form documentation +├── inst/ # Package assets +├── DESCRIPTION # Package metadata +├── NAMESPACE # Exports and imports +├── NEWS.md # Change log +├── README.md # Package overview +├── _pkgdown.yml # Documentation website config +├── .lintr # Linting configuration +└── .Rbuildignore # Build exclusions +``` + +### Naming Conventions + +- **Package names**: Use `teal.` prefix for ecosystem packages (e.g., + `teal.widgets`) +- **Function names**: Use `snake_case` consistently +- **Class names**: Use `PascalCase` (e.g., `TealAppDriver`) +- **Module functions**: Prefix UI functions with `ui_` and server + functions with `srv_` +- **Internal functions**: Use descriptive names without export + +### File Organization + +- **One main function per file** when the function is substantial +- **Group related utilities** in shared files (e.g., `utils.R`, + `validations.R`) +- **Module files**: Use pattern `module_.R` for shiny modules +- **Helper functions**: Prefix with the main function they support + +## Code Style and Standards + +### Code Quality + +- **Run pre-commit hooks**: Always run `pre-commit run --all-files` + before committing. Fix any issues it reports - the error messages are + informative and will guide you. +- **Follow tidyverse style**: General R code style follows the tidyverse + style guide. +- **Documentation**: All exported functions must have roxygen2 + documentation. Run `devtools::document()` to update documentation. + +### Formatting + +Formatting rules are configured in the `.lintr` file. + +### Teal-Specific Conventions + +- **Naming**: Follow the naming conventions outlined in the Package + Structure section + +## Dependencies and Imports + +### Dependency Management + +- **Minimize dependencies**: Only add dependencies that provide + significant value +- **Version constraints**: Specify minimum versions for critical + dependencies +- **Ecosystem coherence**: Prefer packages already used within teal + ecosystem + +``` r +# DESCRIPTION example +Depends: + R (>= 4.1), + shiny (>= 1.8.1) +Imports: + checkmate (>= 2.1.0), + rlang (>= 1.0.0), + teal.widgets (>= 0.5.0) +Suggests: + testthat (>= 3.2.0), + knitr, + rmarkdown +``` + +### Import Best Practices + +``` r +# In NAMESPACE, prefer specific imports over full package imports +#' @importFrom shiny moduleServer NS tagList +#' @importFrom checkmate assert_character assert_function +#' @import teal.data # Only for core teal packages + +# In code, use explicit namespacing for clarity when appropriate +checkmate::assert_string(label) +``` + +## Modules Development + +### Module features + +Each module should produce one or more Table, Listing, or Graph (TLG): + +- **Reproducibility**: All code being executed to generate TLGs should + be run using `teal_data` and `within()` / `teal.code::eval_code()` + - At the end of the module this object should be returned to enable + Reporter and “Show R code” functionalities +- **User Parameters**: Configurable inputs via `teal.picks::picks()` for + flexible data selection +- **Transformators**: Optional pre-processing functions that derive + variables and validate data before analysis +- **Decorators**: Optional post-processing functions that customize + output presentation (titles, legends, annotations) + +### Module Architecture + +Teal modules follow a specific pattern with UI and server components: + +``` r +# UI Function +ui_example_module <- function(id, var_x, var_y, decorators) { + ns <- shiny::NS(id) + select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions + + shiny::tagList( + # Input controls + teal.widgets::standard_layout( + # Output displays + output = teal.widgets::white_small_well( + teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "plot")), + teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "table")), + shiny::tags$h4("Results"), + shiny::plotOutput(ns("plot")), + shiny::tags$h4("Summary data"), + gt::gt_output(ns("table")) + ), + # Encoding panel + encoding = shiny::tags$div( + shiny::tags$label("Encodings", class = "text-primary"), + shiny::tags$br(), + shiny::tags$div( + shiny::tags$strong("Select X-Axis Variable"), + teal.picks::picks_ui(ns("var_x"), var_x) + ), + shiny::tags$div( + shiny::tags$strong("Select Y-Axis Variable"), + teal.picks::picks_ui(ns("var_y"), var_y) + ) + ) + ) + ) +} + +# Server Function +srv_example_module <- function(id, data, var_x, var_y, decorators) { + checkmate::assert_string(id) + checkmate::assert_class(data, "reactive") + + select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions + shiny::moduleServer(id, function(input, output, session) { + selectors <- teal.picks::picks_srv("picks", picks = list(var_x = var_x, var_y = var_y), data = data) + merged <- teal.picks::merge_srv( + "merge_picks", + data = data, + selectors = selectors, + output_name = "anl", + join_fun = "dplyr::inner_join" + ) + # Data preparation + validated_q <- shiny::reactive({ + shiny::validate( + teal::need_input( + inputId = "var_x-variables-selected", + condition = length(selectors$var_x()$variables$selected) > 0, + message = "X-Axis Variable must be selected" + ), + teal::need_input( + inputId = "var_y-variables-selected", + condition = length(selectors$var_y()$variables$selected) > 0, + message = "Y-Axis Variable must be selected" + ) + ) + shiny::validate( + teal::need_input( + inputId = c("var_x-variables-selected", "var_y-variables-selected"), + condition = !any(selectors$var_x()$variables$selected %in% selectors$var_y()$variables$selected), + message = "X-axis variable and Y-axis variable must be different" + ) + ) + q <- merged$data() + teal.reporter::teal_card(q) <- c(teal.reporter::teal_card(q), "## Module's output") + q + }) + + # Generate plot inside qenv + qenv_plot <- reactive({ + within(validated_q(), { + plot <- ggplot2::ggplot(anl) + + ggplot2::geom_point(ggplot2::aes(x = env_var_x, y = env_var_y)) + }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) + }) + decorated_plot <- teal::srv_transform_teal_data( + "decorator_table", + qenv_plot, + select_decorators(decorators, "plot"), + expr = quote(plot) + ) + + qenv_table <- reactive({ + within(validated_q(), { + table <- gtsummary::tbl_summary(anl, by = env_var_x, missing = "no") + }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) + }) + decorated_table <- teal::srv_transform_teal_data( + "decorator_table", + qenv_table, + select_decorators(decorators, "table"), + expr = quote(table) + ) + + # Output rendering: use ggplot2 for visualizations + output$plot <- shiny::renderPlot(decorated_plot()[["plot"]]) + output$table <- gt::render_gt(expr = gtsummary::as_gt(decorated_table()[["table"]])) + # Return reactive + + reactive(c(decorated_plot(), decorated_table())) + }) +} + +tm_example_module <- function( + label = "Example Module", + var_x = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 1L)), + var_y = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 2L)), + decorators = list(), + transformators = list() +) { + checkmate::assert_string(label) + checkmate::assert_class(var_x, "picks") + checkmate::assert_class(var_y, "picks") + checkmate::assert_list(transformators, types = "teal_transform_module") + args <- list(var_x = var_x, var_y = var_y, decorators = decorators) + teal::module( + label = label, + server = srv_example_module, + ui = ui_example_module, + ui_args = args[names(args) %in% names(formals(ui_example_module))], + server_args = args[names(args) %in% names(formals(srv_example_module))], + transformators = transformators + ) +} +``` + +### Code Style for Modules + +- **Use tidyverse style**: Write clear, readable code using dplyr, + ggplot2 patterns +- **Use maggritr pipes in reproducible execution**: For code executed + for `teal_data`/`qenv` data objects with `eval_code()` and `within()` +- **Prefer ggplot2**: For all visualizations over base R plotting +- **Use gt and gtsummary**: For statistical tables and summaries +- **Error handling**: Implement proper validation using `checkmate` and + `shiny::validate(teal::need_input(...))` + +``` r +# Good: Clear data manipulation +plot_data <- data %>% + dplyr::filter(!is.na(variable)) %>% + dplyr::group_by(category) %>% + dplyr::summarise( + mean_value = mean(value), + n = dplyr::n(), + .groups = "drop" + ) + +# Good: Descriptive ggplot2 code +ggplot2::ggplot(plot_data, ggplot2::aes(x = category, y = mean_value)) + + ggplot2::geom_col(fill = "steelblue") + + ggplot2::labs( + title = "Mean Values by Category", + x = "Category", + y = "Mean Value" + ) + + ggplot2::theme_minimal() +``` + +## Testing Framework + +### Testing Philosophy + +- **Test public functions only**: Internal utilities should be tested + through public interfaces +- **Precise, focused tests**: Each test should verify one specific + behavior +- **High coverage**: Maintain at least 80% test coverage as measured by + `covr` +- **Integration over units**: Test realistic usage patterns +- **Test Dependencies**.: Add + `testthat::skip_if_not_installed(package_name)` only for dependencies + in SUGGESTS or related to tests cases + +### Test Structure + +Follow the established patterns from `test-module_teal.R`: + +``` r +# Test organization +testthat::test_that("function_name works with valid inputs", { + # Setup + test_data <- data.frame(x = 1:10, y = rnorm(10)) + + # Execution + result <- function_name(test_data) + + # Verification - one expectation per test preferably + testthat::expect_s3_class(result, "data.frame") +}) + +testthat::test_that("function_name handles edge cases", { + # Test empty input + testthat::expect_error( + function_name(data.frame()), + "Input data cannot be empty" + ) +}) + +testthat::test_that("function_name validates input types", { + # Test invalid input type + testthat::expect_error( + function_name("not a data frame"), + class = "checkmate_error" + ) +}) +``` + +### Shiny Module Testing + +- **Server functions**: Test with `shiny::testServer()` +- **UI functions**: Test basic usage with regular testing (class checks, + error generation, snapshots, regexp search). Test UI scenarios and + interactions with `TealAppDriver` (based on `shinytest2::AppDriver`) + for integration testing +- **Reactive behavior**: Test reactive chains and side effects + +``` r +testthat::test_that("srv_my_module processes data correctly", { + # Test server logic + shiny::testServer( + app = srv_my_module, + args = list( + data = reactive(test_data), + filter_panel_api = NULL + ), + expr = { + # Test reactive computations + result <- processed_data() + testthat::expect_s3_class(result, "teal_data") + } + ) +}) + +testthat::test_that("my_module UI renders correctly", { + # Integration test with TealAppDriver + app <- init( + data = teal_data(mtcars = mtcars), + modules = my_module() + ) + + driver <- TealAppDriver$new(app) + withr::defer(driver$stop()) + driver$navigate_teal_tab("My Module") + + # Test UI elements are present + driver$expect_visible("#plot") +}) +``` + +### Test Organization and Naming + +- **One test file per R file**: `test-module_example.R` for + `module_example.R` +- **Descriptive test names**: Clearly describe what is being tested +- **End to end test names**: `test-shinytest2-module_example.R` for + `module_example.R` +- **Logical grouping**: Group related tests using `describe()` when + beneficial +- **Test data**: Create minimal test datasets, avoid external + dependencies + +## Documentation and Communication + +### Package Documentation + +- **README.md**: Clear overview, installation, basic usage examples +- **Vignettes**: Comprehensive guides for complex functionality +- **Function documentation**: All exported functions must have roxygen2 + documentation +- **NEWS.md**: Detailed changelog following semantic versioning + +### Website Generation + +Use `_pkgdown.yml` for documentation websites: + +``` yaml +url: https://insightsengineering.github.io/package.name + +template: + package: nesttemplate + +reference: + - title: "Main Functions" + contents: + - init + - module + - title: "Helper Functions" + contents: + - starts_with("validate_") +``` + +### Version Management + +Do not change versions on your own. + +## CI/CD and Development Workflow + +### GitHub Workflows + +Use `r.pkg.template` workflows for consistency: + +- **check.yaml**: R CMD check, unit tests, coverage +- **docs.yaml**: Documentation building and deployment +- **audit.yaml**: Security and dependency auditing +- **pkgdown.yaml**: Website generation + +### Pre-commit Hooks + +**Always run pre-commit before committing code**: + +``` bash +pre-commit run --all-files +``` + +Fix any issues that pre-commit reports. The error messages are +informative and will guide you on what needs to be fixed. Pre-commit +automatically checks code style, documentation, linting, and other +quality issues. + +### Dependency Management with Staged Dependencies + +`staged_dependencies.yaml` is an old artifact. Ignore it. + +## Quality Assurance + +### Code Quality Metrics + +- **Test Coverage**: ≥80% line coverage +- **Linting**: No lint violations using configured `.lintr` +- **Documentation**: 100% of exports documented +- **Dependencies**: Minimal and justified dependencies only + +### Code Review Process + +- **Pull Request Reviews**: All changes require review +- **Automated Checks**: CI must pass before merging +- **Breaking Changes**: Require special consideration and communication +- **Documentation Updates**: Must accompany functional changes + +### Performance Considerations + +- **Shiny Reactivity**: Minimize unnecessary reactive computations +- **Data Processing**: Use efficient data manipulation patterns +- **Memory Usage**: Consider memory implications for large datasets +- **Loading Time**: Optimize package loading and module initialization + +## Maintenance Guidelines + +- **Long-term Support**: Maintain backward compatibility when possible +- **Deprecation**: Use `lifecycle` package for function deprecation + +This guide ensures consistency, quality, and maintainability across the +teal ecosystem while following R community best practices. From f1a471b47327d874bcbf74f506a6ace842503c41 Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 25 Sep 2026 13:03:18 +0200 Subject: [PATCH 02/20] docs: add full intstructions to CLAUDE.Rmd --- CLAUDE.Rmd | 41 ++++- CLAUDE.md | 489 ++++++++++------------------------------------------- 2 files changed, 126 insertions(+), 404 deletions(-) diff --git a/CLAUDE.Rmd b/CLAUDE.Rmd index b1becc63..4bb36d5b 100644 --- a/CLAUDE.Rmd +++ b/CLAUDE.Rmd @@ -7,12 +7,49 @@ output: github_document knitr::opts_chunk$set(echo = TRUE) ``` - +## Package Overview + +`teal.code` is an R package for storing code and its execution environment in objects called `qenv`. +It supports reproducible code execution in teal Shiny applications. Developers can inspect and modify `qenv` objects, +providing full control over the stored code and environment. These objects also capture warnings and errors automatically. + +## Development Context + +The teal framework uses Shiny to create reproducible environments for data analysis. Within this framework, `teal.code` +manages code and its execution environment. Related packages include: + +- `teal.reporter`: Integrates reports with reproducible code in teal applications. Its reporter functions accept `qenv` + objects as inputs. +- `teal`: Provides the core architecture for teal Shiny applications. Teal modules should use `qenv` objects to ensure + that code changes are recorded and reproducible. + +For an introduction to `qenv` and its related functions and methods, see @vignettes/qenv.Rmd. + +### Workflows + +- When creating a `qenv`, favor readability over concise code. +- For simple `qenv` objects, favor `within()`, as it supports more readable code. +- When creating a `qenv` that uses functions with side effects, such as `runif()`, always use `eval_code()`. +- For a complex `qenv`, consider dividing the work into smaller `qenv` objects that encapsulate distinct logical steps. +- For a long or complex `qenv`, choose between `eval_code()` and `within()` based on: + - the number of external variables added to the `qenv`; a larger number favors `eval_code()`; + - how often the same external variable is used; greater repetition favors `within()`. +- When fixing bugs or adding features, avoid changing the core principles of `qenv`. Check whether an issue already + exists. If the expected behavior is unclear, request that an issue be created before making changes that could have + unexpected consequences. ```{r teal_content, echo=FALSE, results='asis'} -common_claude_url <- "https://raw.githubusercontent.com/insightsengineering/teal/main/AGENTS.md" +# Append common shared instructions from teal framework +common_claude_url <- "https://raw.githubusercontent.com/insightsengineering/teal/refs/heads/add_common_claude%40main/SHARED_CLAUDE.md" # nolint: line_length_linter. common_claude_path <- tempfile() download.file(url = common_claude_url, destfile = common_claude_path, quiet = TRUE) common_claude_shared <- readLines(common_claude_path) +# remove CI gitlab content and github section comments +gitlab_starts <- grep("gitlab starts here", common_claude_shared) +gitlab_ends <- grep("gitlab ends here", common_claude_shared) +common_claude_shared <- common_claude_shared[-1*seq(gitlab_starts, gitlab_ends)] +github_starts <- grep("github starts here", common_claude_shared) +github_ends <- grep("github ends here", common_claude_shared) +common_claude_shared <- common_claude_shared[-1*c(github_starts, github_ends)] cat(common_claude_shared, sep = "\n") ``` \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index ff938f1f..42bc6b7f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,79 +1,76 @@ teal.code R Package Development Guide ================ - - -# Teal Ecosystem R Package Development Guide - -## Introduction - -The teal framework uses shiny to create reproducible environments for -analysts. The ecosystem comprises several interconnected packages with -specific roles: - -### Core Packages - -- **teal** - The main framework package providing the application - structure -- **teal.code** - Bare code generation and evaluation ensuring - reproducibility -- **teal.data** - Data management and relationships between datasets - (contains sample data for ADaM datasets and default keys to merge ADaM - datasets) -- **teal.reporter** - Report generation functionality -- **teal.slice** - Data filtering capabilities for application -- **teal.widgets** - Reusable UI components -- **teal.logger** - Standardized logging across the framework -- **teal.picks** - Data selection and merging utilities using - `teal.data` objects -- **teal.transform** - Data transformation utilities (deprecated in - favor of teal.picks) - -### Module Packages - -- **teal.modules.general** (tmg) - General-purpose analysis modules -- **teal.modules.clinical** (tmc) - Clinical trial specific modules -- **teal.modules.hermes** - MultiAssayExperiment analysis modules -- **teal.goshawk** - Pharmacokinetics analysis modules -- **teal.osprey** - Advanced clinical analysis modules - -### Supporting Packages - -- **tern** - Statistical analysis functions -- **rtables** - Table creation and formatting -- **formatters** - Output formatting utilities -- **gtsummary** - Table creation and formatting - -**Key Principle**: Balance dependency value with features. Minimize -dependencies to packages not already in use within the ecosystem. +## Package Overview + +`teal.code` is an R package for storing code and its execution +environment in objects called `qenv`. It supports reproducible code +execution in teal Shiny applications. Developers can inspect and modify +`qenv` objects, providing full control over the stored code and +environment. These objects also capture warnings and errors +automatically. + +## Development Context + +The teal framework uses Shiny to create reproducible environments for +data analysis. Within this framework, `teal.code` manages code and its +execution environment. Related packages include: + +- `teal.reporter`: Integrates reports with reproducible code in teal + applications. Its reporter functions accept `qenv` objects as inputs. +- `teal`: Provides the core architecture for teal Shiny applications. + Teal modules should use `qenv` objects to ensure that code changes are + recorded and reproducible. + +For an introduction to `qenv` and its related functions and methods, see +@vignettes/qenv.Rmd. + +### Workflows + +- When creating a `qenv`, favor readability over concise code. +- For simple `qenv` objects, favor `within()`, as it supports more + readable code. +- When creating a `qenv` that uses functions with side effects, such as + `runif()`, always use `eval_code()`. +- For a complex `qenv`, consider dividing the work into smaller `qenv` + objects that encapsulate distinct logical steps. +- For a long or complex `qenv`, choose between `eval_code()` and + `within()` based on: - the number of external variables added to the + `qenv`; a larger number favors `eval_code()`; - how often the same + external variable is used; greater repetition favors `within()`. +- When fixing bugs or adding features, avoid changing the core + principles of `qenv`. Check whether an issue already exists. If the + expected behavior is unclear, request that an issue be created before + making changes that could have unexpected consequences. + +This package is part of the teal framework. The following configuration +applies to all packages within the teal framework: ## Package Structure and Organization -### Standard Package Layout +### Key Directories Follow the standard R package structure with teal-specific conventions: ``` text package_name/ -├── .github/workflows/ # CI/CD workflows (use r.pkg.template) -├── R/ # R source code -├── tests/testthat/ # Unit tests -├── man/ # Documentation -├── vignettes/ # Long-form documentation -├── inst/ # Package assets -├── DESCRIPTION # Package metadata -├── NAMESPACE # Exports and imports -├── NEWS.md # Change log -├── README.md # Package overview -├── _pkgdown.yml # Documentation website config -├── .lintr # Linting configuration -└── .Rbuildignore # Build exclusions +├── .gitlab-ci.yml # CI/CD workflows +├── R/ # R source code +├── tests/testthat/ # Unit tests using testthat +├── vignettes/ # Long-form documentation +├── inst/ # Package assets +├── CLAUDE.md # Development guide for AI agents (this file) +├── DESCRIPTION # Package metadata +├── NAMESPACE # Exports and imports automa +├── NEWS.md # Change log +├── README.md # Package overview +├── _pkgdown.yml # Documentation website config +├── .lintr # Linting configuration +└── .Rbuildignore # Build exclusions ``` ### Naming Conventions -- **Package names**: Use `teal.` prefix for ecosystem packages (e.g., - `teal.widgets`) - **Function names**: Use `snake_case` consistently - **Class names**: Use `PascalCase` (e.g., `TealAppDriver`) - **Module functions**: Prefix UI functions with `ui_` and server @@ -85,29 +82,22 @@ package_name/ - **One main function per file** when the function is substantial - **Group related utilities** in shared files (e.g., `utils.R`, `validations.R`) -- **Module files**: Use pattern `module_.R` for shiny modules +- **Module files**: Use pattern `tm_.R` for teal modules - **Helper functions**: Prefix with the main function they support ## Code Style and Standards ### Code Quality -- **Run pre-commit hooks**: Always run `pre-commit run --all-files` +- **Run `pre-commit` hooks**: Always run `pre-commit run --all-files` before committing. Fix any issues it reports - the error messages are - informative and will guide you. -- **Follow tidyverse style**: General R code style follows the tidyverse - style guide. -- **Documentation**: All exported functions must have roxygen2 + informative and will guide you. It automatically checks code style, + documentation, linting, and other quality issues. +- **Follow `tidyverse` style**: General R code style follows the + `tidyverse` style guide. +- **Documentation**: All exported functions must have `roxygen2` documentation. Run `devtools::document()` to update documentation. - -### Formatting - -Formatting rules are configured in the `.lintr` file. - -### Teal-Specific Conventions - -- **Naming**: Follow the naming conventions outlined in the Package - Structure section +- **Formatting** rules are configured in the `.lintr` file. ## Dependencies and Imports @@ -120,221 +110,22 @@ Formatting rules are configured in the `.lintr` file. - **Ecosystem coherence**: Prefer packages already used within teal ecosystem -``` r -# DESCRIPTION example -Depends: - R (>= 4.1), - shiny (>= 1.8.1) -Imports: - checkmate (>= 2.1.0), - rlang (>= 1.0.0), - teal.widgets (>= 0.5.0) -Suggests: - testthat (>= 3.2.0), - knitr, - rmarkdown -``` - ### Import Best Practices -``` r -# In NAMESPACE, prefer specific imports over full package imports -#' @importFrom shiny moduleServer NS tagList -#' @importFrom checkmate assert_character assert_function -#' @import teal.data # Only for core teal packages - -# In code, use explicit namespacing for clarity when appropriate -checkmate::assert_string(label) -``` - -## Modules Development - -### Module features - -Each module should produce one or more Table, Listing, or Graph (TLG): - -- **Reproducibility**: All code being executed to generate TLGs should - be run using `teal_data` and `within()` / `teal.code::eval_code()` - - At the end of the module this object should be returned to enable - Reporter and “Show R code” functionalities -- **User Parameters**: Configurable inputs via `teal.picks::picks()` for - flexible data selection -- **Transformators**: Optional pre-processing functions that derive - variables and validate data before analysis -- **Decorators**: Optional post-processing functions that customize - output presentation (titles, legends, annotations) - -### Module Architecture - -Teal modules follow a specific pattern with UI and server components: - -``` r -# UI Function -ui_example_module <- function(id, var_x, var_y, decorators) { - ns <- shiny::NS(id) - select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions - - shiny::tagList( - # Input controls - teal.widgets::standard_layout( - # Output displays - output = teal.widgets::white_small_well( - teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "plot")), - teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "table")), - shiny::tags$h4("Results"), - shiny::plotOutput(ns("plot")), - shiny::tags$h4("Summary data"), - gt::gt_output(ns("table")) - ), - # Encoding panel - encoding = shiny::tags$div( - shiny::tags$label("Encodings", class = "text-primary"), - shiny::tags$br(), - shiny::tags$div( - shiny::tags$strong("Select X-Axis Variable"), - teal.picks::picks_ui(ns("var_x"), var_x) - ), - shiny::tags$div( - shiny::tags$strong("Select Y-Axis Variable"), - teal.picks::picks_ui(ns("var_y"), var_y) - ) - ) - ) - ) -} - -# Server Function -srv_example_module <- function(id, data, var_x, var_y, decorators) { - checkmate::assert_string(id) - checkmate::assert_class(data, "reactive") - - select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions - shiny::moduleServer(id, function(input, output, session) { - selectors <- teal.picks::picks_srv("picks", picks = list(var_x = var_x, var_y = var_y), data = data) - merged <- teal.picks::merge_srv( - "merge_picks", - data = data, - selectors = selectors, - output_name = "anl", - join_fun = "dplyr::inner_join" - ) - # Data preparation - validated_q <- shiny::reactive({ - shiny::validate( - teal::need_input( - inputId = "var_x-variables-selected", - condition = length(selectors$var_x()$variables$selected) > 0, - message = "X-Axis Variable must be selected" - ), - teal::need_input( - inputId = "var_y-variables-selected", - condition = length(selectors$var_y()$variables$selected) > 0, - message = "Y-Axis Variable must be selected" - ) - ) - shiny::validate( - teal::need_input( - inputId = c("var_x-variables-selected", "var_y-variables-selected"), - condition = !any(selectors$var_x()$variables$selected %in% selectors$var_y()$variables$selected), - message = "X-axis variable and Y-axis variable must be different" - ) - ) - q <- merged$data() - teal.reporter::teal_card(q) <- c(teal.reporter::teal_card(q), "## Module's output") - q - }) - - # Generate plot inside qenv - qenv_plot <- reactive({ - within(validated_q(), { - plot <- ggplot2::ggplot(anl) + - ggplot2::geom_point(ggplot2::aes(x = env_var_x, y = env_var_y)) - }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) - }) - decorated_plot <- teal::srv_transform_teal_data( - "decorator_table", - qenv_plot, - select_decorators(decorators, "plot"), - expr = quote(plot) - ) - - qenv_table <- reactive({ - within(validated_q(), { - table <- gtsummary::tbl_summary(anl, by = env_var_x, missing = "no") - }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) - }) - decorated_table <- teal::srv_transform_teal_data( - "decorator_table", - qenv_table, - select_decorators(decorators, "table"), - expr = quote(table) - ) - - # Output rendering: use ggplot2 for visualizations - output$plot <- shiny::renderPlot(decorated_plot()[["plot"]]) - output$table <- gt::render_gt(expr = gtsummary::as_gt(decorated_table()[["table"]])) - # Return reactive - - reactive(c(decorated_plot(), decorated_table())) - }) -} - -tm_example_module <- function( - label = "Example Module", - var_x = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 1L)), - var_y = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 2L)), - decorators = list(), - transformators = list() -) { - checkmate::assert_string(label) - checkmate::assert_class(var_x, "picks") - checkmate::assert_class(var_y, "picks") - checkmate::assert_list(transformators, types = "teal_transform_module") - args <- list(var_x = var_x, var_y = var_y, decorators = decorators) - teal::module( - label = label, - server = srv_example_module, - ui = ui_example_module, - ui_args = args[names(args) %in% names(formals(ui_example_module))], - server_args = args[names(args) %in% names(formals(srv_example_module))], - transformators = transformators - ) -} -``` +Avoid importing package functions via roxygen2 (`#' @import pkg`)tags in +favor of explicit namespacing for clarity when appropriate. When needed +prefer specific imports over full package imports. ### Code Style for Modules -- **Use tidyverse style**: Write clear, readable code using dplyr, - ggplot2 patterns -- **Use maggritr pipes in reproducible execution**: For code executed +- **Use `tidyverse` style**: Write clear, readable code using `dplyr`, + `ggplot2` patterns +- **Use `magrittr` pipes in reproducible execution**: For code executed for `teal_data`/`qenv` data objects with `eval_code()` and `within()` -- **Prefer ggplot2**: For all visualizations over base R plotting -- **Use gt and gtsummary**: For statistical tables and summaries +- **Use crane and gtsummary**: For statistical tables and summaries - **Error handling**: Implement proper validation using `checkmate` and `shiny::validate(teal::need_input(...))` -``` r -# Good: Clear data manipulation -plot_data <- data %>% - dplyr::filter(!is.na(variable)) %>% - dplyr::group_by(category) %>% - dplyr::summarise( - mean_value = mean(value), - n = dplyr::n(), - .groups = "drop" - ) - -# Good: Descriptive ggplot2 code -ggplot2::ggplot(plot_data, ggplot2::aes(x = category, y = mean_value)) + - ggplot2::geom_col(fill = "steelblue") + - ggplot2::labs( - title = "Mean Values by Category", - x = "Category", - y = "Mean Value" - ) + - ggplot2::theme_minimal() -``` - ## Testing Framework ### Testing Philosophy @@ -350,82 +141,15 @@ ggplot2::ggplot(plot_data, ggplot2::aes(x = category, y = mean_value)) + `testthat::skip_if_not_installed(package_name)` only for dependencies in SUGGESTS or related to tests cases -### Test Structure - -Follow the established patterns from `test-module_teal.R`: - -``` r -# Test organization -testthat::test_that("function_name works with valid inputs", { - # Setup - test_data <- data.frame(x = 1:10, y = rnorm(10)) - - # Execution - result <- function_name(test_data) - - # Verification - one expectation per test preferably - testthat::expect_s3_class(result, "data.frame") -}) - -testthat::test_that("function_name handles edge cases", { - # Test empty input - testthat::expect_error( - function_name(data.frame()), - "Input data cannot be empty" - ) -}) - -testthat::test_that("function_name validates input types", { - # Test invalid input type - testthat::expect_error( - function_name("not a data frame"), - class = "checkmate_error" - ) -}) -``` - ### Shiny Module Testing - **Server functions**: Test with `shiny::testServer()` - **UI functions**: Test basic usage with regular testing (class checks, error generation, snapshots, regexp search). Test UI scenarios and - interactions with `TealAppDriver` (based on `shinytest2::AppDriver`) - for integration testing + interactions with `teal::TealAppDriver` (based on + `shinytest2::AppDriver`) for integration testing - **Reactive behavior**: Test reactive chains and side effects -``` r -testthat::test_that("srv_my_module processes data correctly", { - # Test server logic - shiny::testServer( - app = srv_my_module, - args = list( - data = reactive(test_data), - filter_panel_api = NULL - ), - expr = { - # Test reactive computations - result <- processed_data() - testthat::expect_s3_class(result, "teal_data") - } - ) -}) - -testthat::test_that("my_module UI renders correctly", { - # Integration test with TealAppDriver - app <- init( - data = teal_data(mtcars = mtcars), - modules = my_module() - ) - - driver <- TealAppDriver$new(app) - withr::defer(driver$stop()) - driver$navigate_teal_tab("My Module") - - # Test UI elements are present - driver$expect_visible("#plot") -}) -``` - ### Test Organization and Naming - **One test file per R file**: `test-module_example.R` for @@ -442,63 +166,27 @@ testthat::test_that("my_module UI renders correctly", { ### Package Documentation -- **README.md**: Clear overview, installation, basic usage examples +- **`README.md`**: Clear overview, installation, basic usage examples - **Vignettes**: Comprehensive guides for complex functionality -- **Function documentation**: All exported functions must have roxygen2 - documentation -- **NEWS.md**: Detailed changelog following semantic versioning - -### Website Generation - -Use `_pkgdown.yml` for documentation websites: - -``` yaml -url: https://insightsengineering.github.io/package.name - -template: - package: nesttemplate +- **Function documentation**: All exported functions must have + `roxygen2` documentation +- **`NEWS.md`**: Detailed changelog following semantic versioning -reference: - - title: "Main Functions" - contents: - - init - - module - - title: "Helper Functions" - contents: - - starts_with("validate_") -``` - -### Version Management +### Package Version Management -Do not change versions on your own. +Do not change versions on your own. There is a CI/CD workflow that +manages the versions automatically on the `main` branch. ## CI/CD and Development Workflow ### GitHub Workflows -Use `r.pkg.template` workflows for consistency: - -- **check.yaml**: R CMD check, unit tests, coverage -- **docs.yaml**: Documentation building and deployment -- **audit.yaml**: Security and dependency auditing -- **pkgdown.yaml**: Website generation - -### Pre-commit Hooks - -**Always run pre-commit before committing code**: +Use r.pkg.template workflows for consistency: -``` bash -pre-commit run --all-files -``` - -Fix any issues that pre-commit reports. The error messages are -informative and will guide you on what needs to be fixed. Pre-commit -automatically checks code style, documentation, linting, and other -quality issues. - -### Dependency Management with Staged Dependencies - -`staged_dependencies.yaml` is an old artifact. Ignore it. +- `check.yaml`: R CMD check, unit tests, coverage +- `docs.yaml`: Documentation building and deployment +- `audit.yaml`: Security and dependency auditing +- `pkgdown.yaml`: Website generation ## Quality Assurance @@ -527,6 +215,3 @@ quality issues. - **Long-term Support**: Maintain backward compatibility when possible - **Deprecation**: Use `lifecycle` package for function deprecation - -This guide ensures consistency, quality, and maintainability across the -teal ecosystem while following R community best practices. From 2e980f177cd0009f4a9a3699d7c09ec3800b7018 Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 25 Sep 2026 13:08:03 +0200 Subject: [PATCH 03/20] chore: update config for verified commits From e70a4fb219b6d9a0cb15e1e876e7cda57118555a Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 11:10:34 +0000 Subject: [PATCH 04/20] [skip style] [skip vbump] Restyle files --- CLAUDE.Rmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CLAUDE.Rmd b/CLAUDE.Rmd index 4bb36d5b..aa67569a 100644 --- a/CLAUDE.Rmd +++ b/CLAUDE.Rmd @@ -47,9 +47,9 @@ common_claude_shared <- readLines(common_claude_path) # remove CI gitlab content and github section comments gitlab_starts <- grep("gitlab starts here", common_claude_shared) gitlab_ends <- grep("gitlab ends here", common_claude_shared) -common_claude_shared <- common_claude_shared[-1*seq(gitlab_starts, gitlab_ends)] +common_claude_shared <- common_claude_shared[-1 * seq(gitlab_starts, gitlab_ends)] github_starts <- grep("github starts here", common_claude_shared) github_ends <- grep("github ends here", common_claude_shared) -common_claude_shared <- common_claude_shared[-1*c(github_starts, github_ends)] +common_claude_shared <- common_claude_shared[-1 * c(github_starts, github_ends)] cat(common_claude_shared, sep = "\n") -``` \ No newline at end of file +``` From 1fafbb6093286ae62b518df69f3c010574691439 Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 25 Sep 2026 13:17:32 +0200 Subject: [PATCH 05/20] chore: update config for verified commits From d4b42137ad2d4cf9f6fb3a291c12a06c7cc2f163 Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 25 Sep 2026 13:25:54 +0200 Subject: [PATCH 06/20] chore: include the CLAUDE.md in the Rbuildignore --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index 5164b2a4..9803b731 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -8,6 +8,7 @@ SECURITY.md ^Makefile$ ^Meta$ ^TODO\.md$ +CLAUDE.md ^[^/]+\.R$ ^[^/]+\.Rmd$ ^[^/]+\.html$ From 36461ec5a867f0fa80d0b68756646db5340d92cc Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 25 Sep 2026 13:35:05 +0200 Subject: [PATCH 07/20] chore: fix markdown style --- CLAUDE.Rmd | 2 +- CLAUDE.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CLAUDE.Rmd b/CLAUDE.Rmd index aa67569a..5708f075 100644 --- a/CLAUDE.Rmd +++ b/CLAUDE.Rmd @@ -1,7 +1,7 @@ --- -title: "teal.code R Package Development Guide" output: github_document --- +# teal.code R Package Development Guide ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) diff --git a/CLAUDE.md b/CLAUDE.md index 42bc6b7f..7d4a3cc5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,5 @@ -teal.code R Package Development Guide -================ + +# teal.code R Package Development Guide ## Package Overview From 77f24d2d6e5103ecac855fe678e774f8eaa799a6 Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 25 Sep 2026 15:57:05 +0200 Subject: [PATCH 08/20] chore: modify intro sentence to avoid warnings in the template --- CLAUDE.Rmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.Rmd b/CLAUDE.Rmd index 5708f075..1a084013 100644 --- a/CLAUDE.Rmd +++ b/CLAUDE.Rmd @@ -38,6 +38,8 @@ For an introduction to `qenv` and its related functions and methods, see @vignet exists. If the expected behavior is unclear, request that an issue be created before making changes that could have unexpected consequences. +This package is part of the teal framework. The following configuration applies to all packages within the teal framework: + ```{r teal_content, echo=FALSE, results='asis'} # Append common shared instructions from teal framework common_claude_url <- "https://raw.githubusercontent.com/insightsengineering/teal/refs/heads/add_common_claude%40main/SHARED_CLAUDE.md" # nolint: line_length_linter. From f76fad396fb013763091c2d67aed6d97395481a3 Mon Sep 17 00:00:00 2001 From: Oriol Senan <35930244+osenan@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:55:22 +0200 Subject: [PATCH 09/20] Update CLAUDE.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com> Signed-off-by: Oriol Senan <35930244+osenan@users.noreply.github.com> --- CLAUDE.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.Rmd b/CLAUDE.Rmd index 1a084013..3841185b 100644 --- a/CLAUDE.Rmd +++ b/CLAUDE.Rmd @@ -29,7 +29,7 @@ For an introduction to `qenv` and its related functions and methods, see @vignet - When creating a `qenv`, favor readability over concise code. - For simple `qenv` objects, favor `within()`, as it supports more readable code. -- When creating a `qenv` that uses functions with side effects, such as `runif()`, always use `eval_code()`. +- When creating a `qenv` that uses functions with side effects, such as `runif()`, `library` or `options` always use `eval_code()`. Use the appropriate comment to link side effects to objects - For a complex `qenv`, consider dividing the work into smaller `qenv` objects that encapsulate distinct logical steps. - For a long or complex `qenv`, choose between `eval_code()` and `within()` based on: - the number of external variables added to the `qenv`; a larger number favors `eval_code()`; From 723055776051b4705643369b8580f723547aa808 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 29 Sep 2026 15:40:19 +0200 Subject: [PATCH 10/20] refactor, use AGENTS.Rmd within vignettes instead of CLAUDE.Rmd --- .Rbuildignore | 3 +- AGENTS.md | 568 ++++++++++++++++++++++++++++ CLAUDE.md | 217 ----------- CLAUDE.Rmd => vignettes/_AGENTS.Rmd | 45 ++- 4 files changed, 598 insertions(+), 235 deletions(-) create mode 100644 AGENTS.md delete mode 100644 CLAUDE.md rename CLAUDE.Rmd => vignettes/_AGENTS.Rmd (60%) diff --git a/.Rbuildignore b/.Rbuildignore index 9803b731..ae594719 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -8,7 +8,8 @@ SECURITY.md ^Makefile$ ^Meta$ ^TODO\.md$ -CLAUDE.md +AGENTS.md +^vignettes/_AGENTS\.Rmd$ ^[^/]+\.R$ ^[^/]+\.Rmd$ ^[^/]+\.html$ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..a9061fa2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,568 @@ + +# teal.code R Package Development Guide + +## Package Overview + +`teal.code` is an R package for storing code and its execution +environment in objects called `qenv`. It supports reproducible code +execution in teal Shiny applications. Developers can inspect and modify +`qenv` objects, providing full control over the stored code and +environment. These objects also capture warnings and errors +automatically. + +## Development Context + +The teal framework uses Shiny to create reproducible environments for +data analysis. Within this framework, `teal.code` manages code and its +execution environment. Related packages include: + +- `teal.reporter`: Integrates reports with reproducible code in teal + applications. Its reporter functions accept `qenv` objects as inputs. +- `teal`: Provides the core architecture for teal Shiny applications. + Teal modules should use `qenv` objects to ensure that code changes are + recorded and reproducible. + +For an introduction to `qenv` and its related functions and methods, see +@vignettes/qenv.Rmd. + +### Workflows + +- When creating a `qenv`, favor readability over concise code. +- For simple `qenv` objects, favor `within()`, as it supports more + readable code. +- When creating a `qenv` that uses functions with side effects, such as + `runif()`, `library` or `options` always use `eval_code()`. Use the + appropriate comment to link side effects to objects +- For a complex `qenv`, consider dividing the work into smaller `qenv` + objects that encapsulate distinct logical steps. +- For a long or complex `qenv`, choose between `eval_code()` and + `within()` based on: + - the number of external variables added to the `qenv`; a larger + number favors `eval_code()`; + - how often the same external variable is used; greater repetition + favors `within()`. +- When fixing bugs or adding features, avoid changing the core + principles of `qenv`. Check whether an issue already exists. If the + expected behavior is unclear, request that an issue be created before + making changes that could have unexpected consequences. + +This package is part of the teal framework. The following configuration +applies to all packages within the teal framework: + +## Introduction + +The teal framework uses shiny to create reproducible environments for +analysts. The ecosystem comprises several interconnected packages with +specific roles: + +### Core Packages + +- **teal** - The main framework package providing the application + structure +- **teal.code** - Bare code generation and evaluation ensuring + reproducibility +- **teal.data** - Data management and relationships between datasets + (contains sample data for ADaM datasets and default keys to merge ADaM + datasets) +- **teal.reporter** - Report generation functionality +- **teal.slice** - Data filtering capabilities for application +- **teal.widgets** - Reusable UI components +- **teal.logger** - Standardized logging across the framework +- **teal.picks** - Data selection and merging utilities using + `teal.data` objects +- **teal.transform** - Data transformation utilities (deprecated in + favor of teal.picks) + +### Module Packages + +- **teal.modules.general** (tmg) - General-purpose analysis modules +- **teal.modules.clinical** (tmc) - Clinical trial specific modules +- **teal.modules.hermes** - MultiAssayExperiment analysis modules +- **teal.goshawk** - Pharmacokinetics analysis modules +- **teal.osprey** - Advanced clinical analysis modules + +### Supporting Packages + +- **tern** - Statistical analysis functions +- **rtables** - Table creation and formatting +- **formatters** - Output formatting utilities +- **gtsummary** - Table creation and formatting + +**Key Principle**: Balance dependency value with features. Minimize +dependencies to packages not already in use within the ecosystem. + +## Package Structure and Organization + +### Standard Package Layout + +Follow the standard R package structure with teal-specific conventions: + +``` text +package_name/ +├── .github/workflows/ # CI/CD workflows (use r.pkg.template) +├── R/ # R source code +├── tests/testthat/ # Unit tests +├── man/ # Documentation +├── vignettes/ # Long-form documentation +├── inst/ # Package assets +├── DESCRIPTION # Package metadata +├── NAMESPACE # Exports and imports +├── NEWS.md # Change log +├── README.md # Package overview +├── _pkgdown.yml # Documentation website config +├── .lintr # Linting configuration +└── .Rbuildignore # Build exclusions +``` + +### Naming Conventions + +- **Package names**: Use `teal.` prefix for ecosystem packages (e.g., + `teal.widgets`) +- **Function names**: Use `snake_case` consistently +- **Class names**: Use `PascalCase` (e.g., `TealAppDriver`) +- **Module functions**: Prefix UI functions with `ui_` and server + functions with `srv_` +- **Internal functions**: Use descriptive names without export + +### File Organization + +- **One main function per file** when the function is substantial +- **Group related utilities** in shared files (e.g., `utils.R`, + `validations.R`) +- **Module files**: Use pattern `module_.R` for shiny modules +- **Helper functions**: Prefix with the main function they support + +## Code Style and Standards + +### Code Quality + +- **Run pre-commit hooks**: Always run `pre-commit run --all-files` + before committing. Fix any issues it reports - the error messages are + informative and will guide you. +- **Follow tidyverse style**: General R code style follows the tidyverse + style guide. +- **Documentation**: All exported functions must have roxygen2 + documentation. Run `devtools::document()` to update documentation. + +### Formatting + +Formatting rules are configured in the `.lintr` file. + +### Teal-Specific Conventions + +- **Naming**: Follow the naming conventions outlined in the Package + Structure section + +## Dependencies and Imports + +### Dependency Management + +- **Minimize dependencies**: Only add dependencies that provide + significant value +- **Version constraints**: Specify minimum versions for critical + dependencies +- **Ecosystem coherence**: Prefer packages already used within teal + ecosystem + +``` r +Depends: + R (>= 4.1), + shiny (>= 1.8.1) +Imports: + checkmate (>= 2.1.0), + rlang (>= 1.0.0), + teal.widgets (>= 0.5.0) +Suggests: + testthat (>= 3.2.0), + knitr, + rmarkdown +``` + +### Import Best Practices + +``` r +#' @importFrom shiny moduleServer NS tagList +#' @importFrom checkmate assert_character assert_function +#' @import teal.data # Only for core teal packages + +checkmate::assert_string(label) +``` + +## Modules Development + +### Module features + +Each module should produce one or more Table, Listing, or Graph (TLG): + +- **Reproducibility**: All code being executed to generate TLGs should + be run using `teal_data` and `within()` / `teal.code::eval_code()` + - At the end of the module this object should be returned to enable + Reporter and “Show R code” functionalities +- **User Parameters**: Configurable inputs via `teal.picks::picks()` for + flexible data selection +- **Transformators**: Optional pre-processing functions that derive + variables and validate data before analysis +- **Decorators**: Optional post-processing functions that customize + output presentation (titles, legends, annotations) + +### Module Architecture + +Teal modules follow a specific pattern with UI and server components: + +``` r +ui_example_module <- function(id, var_x, var_y, decorators) { + ns <- shiny::NS(id) + select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions + + shiny::tagList( + # Input controls + teal.widgets::standard_layout( + # Output displays + output = teal.widgets::white_small_well( + teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "plot")), + teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "table")), + shiny::tags$h4("Results"), + shiny::plotOutput(ns("plot")), + shiny::tags$h4("Summary data"), + gt::gt_output(ns("table")) + ), + # Encoding panel + encoding = shiny::tags$div( + shiny::tags$label("Encodings", class = "text-primary"), + shiny::tags$br(), + shiny::tags$div( + shiny::tags$strong("Select X-Axis Variable"), + teal.picks::picks_ui(ns("var_x"), var_x) + ), + shiny::tags$div( + shiny::tags$strong("Select Y-Axis Variable"), + teal.picks::picks_ui(ns("var_y"), var_y) + ) + ) + ) + ) +} + +srv_example_module <- function(id, data, var_x, var_y, decorators) { + checkmate::assert_string(id) + checkmate::assert_class(data, "reactive") + + select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions + shiny::moduleServer(id, function(input, output, session) { + selectors <- teal.picks::picks_srv("picks", picks = list(var_x = var_x, var_y = var_y), data = data) + merged <- teal.picks::merge_srv( + "merge_picks", + data = data, + selectors = selectors, + output_name = "anl", + join_fun = "dplyr::inner_join" + ) + # Data preparation + validated_q <- shiny::reactive({ + shiny::validate( + teal::need_input( + inputId = "var_x-variables-selected", + condition = length(selectors$var_x()$variables$selected) > 0, + message = "X-Axis Variable must be selected" + ), + teal::need_input( + inputId = "var_y-variables-selected", + condition = length(selectors$var_y()$variables$selected) > 0, + message = "Y-Axis Variable must be selected" + ) + ) + shiny::validate( + teal::need_input( + inputId = c("var_x-variables-selected", "var_y-variables-selected"), + condition = !any(selectors$var_x()$variables$selected %in% selectors$var_y()$variables$selected), + message = "X-axis variable and Y-axis variable must be different" + ) + ) + q <- merged$data() + teal.reporter::teal_card(q) <- c(teal.reporter::teal_card(q), "## Module's output") + q + }) + + # Generate plot inside qenv + qenv_plot <- reactive({ + within(validated_q(), { + plot <- ggplot2::ggplot(anl) + + ggplot2::geom_point(ggplot2::aes(x = env_var_x, y = env_var_y)) + }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) + }) + decorated_plot <- teal::srv_transform_teal_data( + "decorator_table", + qenv_plot, + select_decorators(decorators, "plot"), + expr = quote(plot) + ) + + qenv_table <- reactive({ + within(validated_q(), { + table <- gtsummary::tbl_summary(anl, by = env_var_x, missing = "no") + }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) + }) + decorated_table <- teal::srv_transform_teal_data( + "decorator_table", + qenv_table, + select_decorators(decorators, "table"), + expr = quote(table) + ) + + # Output rendering: use ggplot2 for visualizations + output$plot <- shiny::renderPlot(decorated_plot()[["plot"]]) + output$table <- gt::render_gt(expr = gtsummary::as_gt(decorated_table()[["table"]])) + # Return reactive + + reactive(c(decorated_plot(), decorated_table())) + }) +} + +tm_example_module <- function( + label = "Example Module", + var_x = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 1L)), + var_y = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 2L)), + decorators = list(), + transformators = list() +) { + checkmate::assert_string(label) + checkmate::assert_class(var_x, "picks") + checkmate::assert_class(var_y, "picks") + checkmate::assert_list(transformators, types = "teal_transform_module") + args <- list(var_x = var_x, var_y = var_y, decorators = decorators) + teal::module( + label = label, + server = srv_example_module, + ui = ui_example_module, + ui_args = args[names(args) %in% names(formals(ui_example_module))], + server_args = args[names(args) %in% names(formals(srv_example_module))], + transformators = transformators + ) +} +``` + +### Code Style for Modules + +- **Use tidyverse style**: Write clear, readable code using dplyr, + ggplot2 patterns +- **Use maggritr pipes in reproducible execution**: For code executed + for `teal_data`/`qenv` data objects with `eval_code()` and `within()` +- **Prefer ggplot2**: For all visualizations over base R plotting +- **Use gt and gtsummary**: For statistical tables and summaries +- **Error handling**: Implement proper validation using `checkmate` and + `shiny::validate(teal::need_input(...))` + +``` r +plot_data <- data %>% + dplyr::filter(!is.na(variable)) %>% + dplyr::group_by(category) %>% + dplyr::summarise( + mean_value = mean(value), + n = dplyr::n(), + .groups = "drop" + ) + +ggplot2::ggplot(plot_data, ggplot2::aes(x = category, y = mean_value)) + + ggplot2::geom_col(fill = "steelblue") + + ggplot2::labs( + title = "Mean Values by Category", + x = "Category", + y = "Mean Value" + ) + + ggplot2::theme_minimal() +``` + +## Testing Framework + +### Testing Philosophy + +- **Test public functions only**: Internal utilities should be tested + through public interfaces +- **Precise, focused tests**: Each test should verify one specific + behavior +- **High coverage**: Maintain at least 80% test coverage as measured by + `covr` +- **Integration over units**: Test realistic usage patterns +- **Test Dependencies**.: Add + `testthat::skip_if_not_installed(package_name)` only for dependencies + in SUGGESTS or related to tests cases + +### Test Structure + +Follow the established patterns from `test-module_teal.R`: + +``` r +testthat::test_that("function_name works with valid inputs", { + # Setup + test_data <- data.frame(x = 1:10, y = rnorm(10)) + + # Execution + result <- function_name(test_data) + + # Verification - one expectation per test preferably + testthat::expect_s3_class(result, "data.frame") +}) + +testthat::test_that("function_name handles edge cases", { + # Test empty input + testthat::expect_error( + function_name(data.frame()), + "Input data cannot be empty" + ) +}) + +testthat::test_that("function_name validates input types", { + # Test invalid input type + testthat::expect_error( + function_name("not a data frame"), + class = "checkmate_error" + ) +}) +``` + +### Shiny Module Testing + +- **Server functions**: Test with `shiny::testServer()` +- **UI functions**: Test basic usage with regular testing (class checks, + error generation, snapshots, regexp search). Test UI scenarios and + interactions with `TealAppDriver` (based on `shinytest2::AppDriver`) + for integration testing +- **Reactive behavior**: Test reactive chains and side effects + +``` r +testthat::test_that("srv_my_module processes data correctly", { + # Test server logic + shiny::testServer( + app = srv_my_module, + args = list( + data = reactive(test_data), + filter_panel_api = NULL + ), + expr = { + # Test reactive computations + result <- processed_data() + testthat::expect_s3_class(result, "teal_data") + } + ) +}) + +testthat::test_that("my_module UI renders correctly", { + # Integration test with TealAppDriver + app <- init( + data = teal_data(mtcars = mtcars), + modules = my_module() + ) + + driver <- TealAppDriver$new(app) + withr::defer(driver$stop()) + driver$navigate_teal_tab("My Module") + + # Test UI elements are present + driver$expect_visible("#plot") +}) +``` + +### Test Organization and Naming + +- **One test file per R file**: `test-module_example.R` for + `module_example.R` +- **Descriptive test names**: Clearly describe what is being tested +- **End to end test names**: `test-shinytest2-module_example.R` for + `module_example.R` +- **Logical grouping**: Group related tests using `describe()` when + beneficial +- **Test data**: Create minimal test datasets, avoid external + dependencies + +## Documentation and Communication + +### Package Documentation + +- **README.md**: Clear overview, installation, basic usage examples +- **Vignettes**: Comprehensive guides for complex functionality +- **Function documentation**: All exported functions must have roxygen2 + documentation +- **NEWS.md**: Detailed changelog following semantic versioning + +### Website Generation + +Use `_pkgdown.yml` for documentation websites: + +``` yaml +url: https://insightsengineering.github.io/package.name + +template: + package: nesttemplate + +reference: + - title: "Main Functions" + contents: + - init + - module + - title: "Helper Functions" + contents: + - starts_with("validate_") +``` + +### Version Management + +Do not change versions on your own. + +## CI/CD and Development Workflow + +### GitHub Workflows + +Use `r.pkg.template` workflows for consistency: + +- **check.yaml**: R CMD check, unit tests, coverage +- **docs.yaml**: Documentation building and deployment +- **audit.yaml**: Security and dependency auditing +- **pkgdown.yaml**: Website generation + +### Pre-commit Hooks + +**Always run pre-commit before committing code**: + +``` bash +pre-commit run --all-files +``` + +Fix any issues that pre-commit reports. The error messages are +informative and will guide you on what needs to be fixed. Pre-commit +automatically checks code style, documentation, linting, and other +quality issues. + +### Dependency Management with Staged Dependencies + +`staged_dependencies.yaml` is an old artifact. Ignore it. + +## Quality Assurance + +### Code Quality Metrics + +- **Test Coverage**: ≥80% line coverage +- **Linting**: No lint violations using configured `.lintr` +- **Documentation**: 100% of exports documented +- **Dependencies**: Minimal and justified dependencies only + +### Code Review Process + +- **Pull Request Reviews**: All changes require review +- **Automated Checks**: CI must pass before merging +- **Breaking Changes**: Require special consideration and communication +- **Documentation Updates**: Must accompany functional changes + +### Performance Considerations + +- **Shiny Reactivity**: Minimize unnecessary reactive computations +- **Data Processing**: Use efficient data manipulation patterns +- **Memory Usage**: Consider memory implications for large datasets +- **Loading Time**: Optimize package loading and module initialization + +## Maintenance Guidelines + +- **Long-term Support**: Maintain backward compatibility when possible +- **Deprecation**: Use `lifecycle` package for function deprecation + +This guide ensures consistency, quality, and maintainability across the +teal ecosystem while following R community best practices. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 7d4a3cc5..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,217 +0,0 @@ - -# teal.code R Package Development Guide - -## Package Overview - -`teal.code` is an R package for storing code and its execution -environment in objects called `qenv`. It supports reproducible code -execution in teal Shiny applications. Developers can inspect and modify -`qenv` objects, providing full control over the stored code and -environment. These objects also capture warnings and errors -automatically. - -## Development Context - -The teal framework uses Shiny to create reproducible environments for -data analysis. Within this framework, `teal.code` manages code and its -execution environment. Related packages include: - -- `teal.reporter`: Integrates reports with reproducible code in teal - applications. Its reporter functions accept `qenv` objects as inputs. -- `teal`: Provides the core architecture for teal Shiny applications. - Teal modules should use `qenv` objects to ensure that code changes are - recorded and reproducible. - -For an introduction to `qenv` and its related functions and methods, see -@vignettes/qenv.Rmd. - -### Workflows - -- When creating a `qenv`, favor readability over concise code. -- For simple `qenv` objects, favor `within()`, as it supports more - readable code. -- When creating a `qenv` that uses functions with side effects, such as - `runif()`, always use `eval_code()`. -- For a complex `qenv`, consider dividing the work into smaller `qenv` - objects that encapsulate distinct logical steps. -- For a long or complex `qenv`, choose between `eval_code()` and - `within()` based on: - the number of external variables added to the - `qenv`; a larger number favors `eval_code()`; - how often the same - external variable is used; greater repetition favors `within()`. -- When fixing bugs or adding features, avoid changing the core - principles of `qenv`. Check whether an issue already exists. If the - expected behavior is unclear, request that an issue be created before - making changes that could have unexpected consequences. - -This package is part of the teal framework. The following configuration -applies to all packages within the teal framework: - -## Package Structure and Organization - -### Key Directories - -Follow the standard R package structure with teal-specific conventions: - -``` text -package_name/ -├── .gitlab-ci.yml # CI/CD workflows -├── R/ # R source code -├── tests/testthat/ # Unit tests using testthat -├── vignettes/ # Long-form documentation -├── inst/ # Package assets -├── CLAUDE.md # Development guide for AI agents (this file) -├── DESCRIPTION # Package metadata -├── NAMESPACE # Exports and imports automa -├── NEWS.md # Change log -├── README.md # Package overview -├── _pkgdown.yml # Documentation website config -├── .lintr # Linting configuration -└── .Rbuildignore # Build exclusions -``` - -### Naming Conventions - -- **Function names**: Use `snake_case` consistently -- **Class names**: Use `PascalCase` (e.g., `TealAppDriver`) -- **Module functions**: Prefix UI functions with `ui_` and server - functions with `srv_` -- **Internal functions**: Use descriptive names without export - -### File Organization - -- **One main function per file** when the function is substantial -- **Group related utilities** in shared files (e.g., `utils.R`, - `validations.R`) -- **Module files**: Use pattern `tm_.R` for teal modules -- **Helper functions**: Prefix with the main function they support - -## Code Style and Standards - -### Code Quality - -- **Run `pre-commit` hooks**: Always run `pre-commit run --all-files` - before committing. Fix any issues it reports - the error messages are - informative and will guide you. It automatically checks code style, - documentation, linting, and other quality issues. -- **Follow `tidyverse` style**: General R code style follows the - `tidyverse` style guide. -- **Documentation**: All exported functions must have `roxygen2` - documentation. Run `devtools::document()` to update documentation. -- **Formatting** rules are configured in the `.lintr` file. - -## Dependencies and Imports - -### Dependency Management - -- **Minimize dependencies**: Only add dependencies that provide - significant value -- **Version constraints**: Specify minimum versions for critical - dependencies -- **Ecosystem coherence**: Prefer packages already used within teal - ecosystem - -### Import Best Practices - -Avoid importing package functions via roxygen2 (`#' @import pkg`)tags in -favor of explicit namespacing for clarity when appropriate. When needed -prefer specific imports over full package imports. - -### Code Style for Modules - -- **Use `tidyverse` style**: Write clear, readable code using `dplyr`, - `ggplot2` patterns -- **Use `magrittr` pipes in reproducible execution**: For code executed - for `teal_data`/`qenv` data objects with `eval_code()` and `within()` -- **Use crane and gtsummary**: For statistical tables and summaries -- **Error handling**: Implement proper validation using `checkmate` and - `shiny::validate(teal::need_input(...))` - -## Testing Framework - -### Testing Philosophy - -- **Test public functions only**: Internal utilities should be tested - through public interfaces -- **Precise, focused tests**: Each test should verify one specific - behavior -- **High coverage**: Maintain at least 80% test coverage as measured by - `covr` -- **Integration over units**: Test realistic usage patterns -- **Test Dependencies**.: Add - `testthat::skip_if_not_installed(package_name)` only for dependencies - in SUGGESTS or related to tests cases - -### Shiny Module Testing - -- **Server functions**: Test with `shiny::testServer()` -- **UI functions**: Test basic usage with regular testing (class checks, - error generation, snapshots, regexp search). Test UI scenarios and - interactions with `teal::TealAppDriver` (based on - `shinytest2::AppDriver`) for integration testing -- **Reactive behavior**: Test reactive chains and side effects - -### Test Organization and Naming - -- **One test file per R file**: `test-module_example.R` for - `module_example.R` -- **Descriptive test names**: Clearly describe what is being tested -- **End to end test names**: `test-shinytest2-module_example.R` for - `module_example.R` -- **Logical grouping**: Group related tests using `describe()` when - beneficial -- **Test data**: Create minimal test datasets, avoid external - dependencies - -## Documentation and Communication - -### Package Documentation - -- **`README.md`**: Clear overview, installation, basic usage examples -- **Vignettes**: Comprehensive guides for complex functionality -- **Function documentation**: All exported functions must have - `roxygen2` documentation -- **`NEWS.md`**: Detailed changelog following semantic versioning - -### Package Version Management - -Do not change versions on your own. There is a CI/CD workflow that -manages the versions automatically on the `main` branch. - -## CI/CD and Development Workflow - -### GitHub Workflows - -Use r.pkg.template workflows for consistency: - -- `check.yaml`: R CMD check, unit tests, coverage -- `docs.yaml`: Documentation building and deployment -- `audit.yaml`: Security and dependency auditing -- `pkgdown.yaml`: Website generation - -## Quality Assurance - -### Code Quality Metrics - -- **Test Coverage**: ≥80% line coverage -- **Linting**: No lint violations using configured `.lintr` -- **Documentation**: 100% of exports documented -- **Dependencies**: Minimal and justified dependencies only - -### Code Review Process - -- **Pull Request Reviews**: All changes require review -- **Automated Checks**: CI must pass before merging -- **Breaking Changes**: Require special consideration and communication -- **Documentation Updates**: Must accompany functional changes - -### Performance Considerations - -- **Shiny Reactivity**: Minimize unnecessary reactive computations -- **Data Processing**: Use efficient data manipulation patterns -- **Memory Usage**: Consider memory implications for large datasets -- **Loading Time**: Optimize package loading and module initialization - -## Maintenance Guidelines - -- **Long-term Support**: Maintain backward compatibility when possible -- **Deprecation**: Use `lifecycle` package for function deprecation diff --git a/CLAUDE.Rmd b/vignettes/_AGENTS.Rmd similarity index 60% rename from CLAUDE.Rmd rename to vignettes/_AGENTS.Rmd index 3841185b..2d23c434 100644 --- a/CLAUDE.Rmd +++ b/vignettes/_AGENTS.Rmd @@ -7,6 +7,20 @@ output: github_document knitr::opts_chunk$set(echo = TRUE) ``` +```{r render, eval=FALSE, include=FALSE} +rmd_path <- "vignettes/_AGENTS.Rmd" + +rmarkdown::render( + input = rmd_path, + output_format = "github_document", + output_file = "AGENTS.md", + output_dir = ".", + quiet = TRUE +) + +file.remove("AGENTS.html") +``` + ## Package Overview `teal.code` is an R package for storing code and its execution environment in objects called `qenv`. @@ -32,26 +46,23 @@ For an introduction to `qenv` and its related functions and methods, see @vignet - When creating a `qenv` that uses functions with side effects, such as `runif()`, `library` or `options` always use `eval_code()`. Use the appropriate comment to link side effects to objects - For a complex `qenv`, consider dividing the work into smaller `qenv` objects that encapsulate distinct logical steps. - For a long or complex `qenv`, choose between `eval_code()` and `within()` based on: - - the number of external variables added to the `qenv`; a larger number favors `eval_code()`; - - how often the same external variable is used; greater repetition favors `within()`. -- When fixing bugs or adding features, avoid changing the core principles of `qenv`. Check whether an issue already - exists. If the expected behavior is unclear, request that an issue be created before making changes that could have - unexpected consequences. + - the number of external variables added to the `qenv`; a larger number favors `eval_code()`; + - how often the same external variable is used; greater repetition favors `within()`. +- When creating custom functions within a teal app server, it is better to do define them inside a qenv. If defined outside +the function might fail. +- When fixing bugs or adding features, avoid changing the core principles of `qenv`. Check whether an issue already exists. If the expected behavior is unclear, request that an issue be created before making changes that could have +unexpected consequences. This package is part of the teal framework. The following configuration applies to all packages within the teal framework: ```{r teal_content, echo=FALSE, results='asis'} # Append common shared instructions from teal framework -common_claude_url <- "https://raw.githubusercontent.com/insightsengineering/teal/refs/heads/add_common_claude%40main/SHARED_CLAUDE.md" # nolint: line_length_linter. -common_claude_path <- tempfile() -download.file(url = common_claude_url, destfile = common_claude_path, quiet = TRUE) -common_claude_shared <- readLines(common_claude_path) -# remove CI gitlab content and github section comments -gitlab_starts <- grep("gitlab starts here", common_claude_shared) -gitlab_ends <- grep("gitlab ends here", common_claude_shared) -common_claude_shared <- common_claude_shared[-1 * seq(gitlab_starts, gitlab_ends)] -github_starts <- grep("github starts here", common_claude_shared) -github_ends <- grep("github ends here", common_claude_shared) -common_claude_shared <- common_claude_shared[-1 * c(github_starts, github_ends)] -cat(common_claude_shared, sep = "\n") +common_agents_url <- "https://raw.githubusercontent.com/insightsengineering/teal/refs/heads/main/AGENTS.md" # nolint: line_length_linter. +common_agents_path <- tempfile() +download.file(url = common_agents_url, destfile = common_agents_path, quiet = TRUE) +common_agents_shared <- readLines(common_agents_path) +# remove a heading line only intented to aovid a markdown lint error +line_to_avoid_lintr <- grep("^# ", common_agents_shared) +common_agents_shared <- common_agents_shared[-1 * line_to_avoid_lintr] +cat(common_agents_shared, sep = "\n") ``` From d1253680b3891ad258fedf98d7dce2cc538fe116 Mon Sep 17 00:00:00 2001 From: osenan Date: Wed, 30 Sep 2026 07:43:31 +0200 Subject: [PATCH 11/20] refactor: improve the way we render AGENTS.md --- AGENTS.md | 467 +++++++----------------------------------- vignettes/_AGENTS.Rmd | 34 ++- 2 files changed, 84 insertions(+), 417 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a9061fa2..bfdaa6a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,10 @@ execution environment. Related packages include: - `teal`: Provides the core architecture for teal Shiny applications. Teal modules should use `qenv` objects to ensure that code changes are recorded and reproducible. +- `teal.data`: Stores all data objects, data relations and + transformations of data used in teal applications. The `teal.data` + object is based on the `qenv` and therefore used for code + reproducibility. For an introduction to `qenv` and its related functions and methods, see @vignettes/qenv.Rmd. @@ -41,83 +45,45 @@ For an introduction to `qenv` and its related functions and methods, see number favors `eval_code()`; - how often the same external variable is used; greater repetition favors `within()`. +- When creating custom functions within a teal app server, it is better + to do define them inside a qenv. If defined outside the function might + fail. - When fixing bugs or adding features, avoid changing the core principles of `qenv`. Check whether an issue already exists. If the expected behavior is unclear, request that an issue be created before making changes that could have unexpected consequences. +### + This package is part of the teal framework. The following configuration -applies to all packages within the teal framework: - -## Introduction - -The teal framework uses shiny to create reproducible environments for -analysts. The ecosystem comprises several interconnected packages with -specific roles: - -### Core Packages - -- **teal** - The main framework package providing the application - structure -- **teal.code** - Bare code generation and evaluation ensuring - reproducibility -- **teal.data** - Data management and relationships between datasets - (contains sample data for ADaM datasets and default keys to merge ADaM - datasets) -- **teal.reporter** - Report generation functionality -- **teal.slice** - Data filtering capabilities for application -- **teal.widgets** - Reusable UI components -- **teal.logger** - Standardized logging across the framework -- **teal.picks** - Data selection and merging utilities using - `teal.data` objects -- **teal.transform** - Data transformation utilities (deprecated in - favor of teal.picks) - -### Module Packages - -- **teal.modules.general** (tmg) - General-purpose analysis modules -- **teal.modules.clinical** (tmc) - Clinical trial specific modules -- **teal.modules.hermes** - MultiAssayExperiment analysis modules -- **teal.goshawk** - Pharmacokinetics analysis modules -- **teal.osprey** - Advanced clinical analysis modules - -### Supporting Packages - -- **tern** - Statistical analysis functions -- **rtables** - Table creation and formatting -- **formatters** - Output formatting utilities -- **gtsummary** - Table creation and formatting - -**Key Principle**: Balance dependency value with features. Minimize -dependencies to packages not already in use within the ecosystem. +applies to all packages within the teal framework. ## Package Structure and Organization -### Standard Package Layout +### Key Directories Follow the standard R package structure with teal-specific conventions: ``` text package_name/ -├── .github/workflows/ # CI/CD workflows (use r.pkg.template) -├── R/ # R source code -├── tests/testthat/ # Unit tests -├── man/ # Documentation -├── vignettes/ # Long-form documentation -├── inst/ # Package assets -├── DESCRIPTION # Package metadata -├── NAMESPACE # Exports and imports -├── NEWS.md # Change log -├── README.md # Package overview -├── _pkgdown.yml # Documentation website config -├── .lintr # Linting configuration -└── .Rbuildignore # Build exclusions +├── .gitlab-ci.yml # CI/CD workflows (if package hosted in Gitlab) +├── .github # CI/CD workflows (if package hosted in GitHub) +├── R/ # R source code +├── tests/testthat/ # Unit tests using testthat +├── vignettes/ # Long-form documentation +├── inst/ # Package assets +├── AGENTS.md # Development guide for AI agents (this file) +├── DESCRIPTION # Package metadata +├── NAMESPACE # Exports and imports automa +├── NEWS.md # Change log +├── README.md # Package overview +├── _pkgdown.yml # Documentation website config +├── .lintr # Linting configuration +└── .Rbuildignore # Build exclusions ``` ### Naming Conventions -- **Package names**: Use `teal.` prefix for ecosystem packages (e.g., - `teal.widgets`) - **Function names**: Use `snake_case` consistently - **Class names**: Use `PascalCase` (e.g., `TealAppDriver`) - **Module functions**: Prefix UI functions with `ui_` and server @@ -129,29 +95,24 @@ package_name/ - **One main function per file** when the function is substantial - **Group related utilities** in shared files (e.g., `utils.R`, `validations.R`) -- **Module files**: Use pattern `module_.R` for shiny modules +- **Module files**: Use pattern `tm_.R` for teal modules - **Helper functions**: Prefix with the main function they support ## Code Style and Standards ### Code Quality -- **Run pre-commit hooks**: Always run `pre-commit run --all-files` - before committing. Fix any issues it reports - the error messages are - informative and will guide you. -- **Follow tidyverse style**: General R code style follows the tidyverse - style guide. -- **Documentation**: All exported functions must have roxygen2 - documentation. Run `devtools::document()` to update documentation. - -### Formatting - -Formatting rules are configured in the `.lintr` file. - -### Teal-Specific Conventions - -- **Naming**: Follow the naming conventions outlined in the Package - Structure section +- **Run `pre-commit` hooks**: Always run `pre-commit run --all-files` + before committing, Fix any issues it reports - the error messages are + informative and will guide you. It automatically checks code style, + documentation and other quality issues. If pre-commit is not + available, run the checks manually. Lint the R code manually as well + if not called by pre-commit. +- **Follow `tidyverse` style**: General R code style follows the + `tidyverse` style guide. +- **Documentation**: All exported functions must have `roxygen2` + documentation with `@returns` and `@examples` fields. +- **Formatting** rules are configured in the `.lintr` file. ## Dependencies and Imports @@ -164,214 +125,22 @@ Formatting rules are configured in the `.lintr` file. - **Ecosystem coherence**: Prefer packages already used within teal ecosystem -``` r -Depends: - R (>= 4.1), - shiny (>= 1.8.1) -Imports: - checkmate (>= 2.1.0), - rlang (>= 1.0.0), - teal.widgets (>= 0.5.0) -Suggests: - testthat (>= 3.2.0), - knitr, - rmarkdown -``` - ### Import Best Practices -``` r -#' @importFrom shiny moduleServer NS tagList -#' @importFrom checkmate assert_character assert_function -#' @import teal.data # Only for core teal packages - -checkmate::assert_string(label) -``` - -## Modules Development - -### Module features - -Each module should produce one or more Table, Listing, or Graph (TLG): - -- **Reproducibility**: All code being executed to generate TLGs should - be run using `teal_data` and `within()` / `teal.code::eval_code()` - - At the end of the module this object should be returned to enable - Reporter and “Show R code” functionalities -- **User Parameters**: Configurable inputs via `teal.picks::picks()` for - flexible data selection -- **Transformators**: Optional pre-processing functions that derive - variables and validate data before analysis -- **Decorators**: Optional post-processing functions that customize - output presentation (titles, legends, annotations) - -### Module Architecture - -Teal modules follow a specific pattern with UI and server components: - -``` r -ui_example_module <- function(id, var_x, var_y, decorators) { - ns <- shiny::NS(id) - select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions - - shiny::tagList( - # Input controls - teal.widgets::standard_layout( - # Output displays - output = teal.widgets::white_small_well( - teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "plot")), - teal::ui_transform_teal_data("decorator_table", select_decorators(decorators, "table")), - shiny::tags$h4("Results"), - shiny::plotOutput(ns("plot")), - shiny::tags$h4("Summary data"), - gt::gt_output(ns("table")) - ), - # Encoding panel - encoding = shiny::tags$div( - shiny::tags$label("Encodings", class = "text-primary"), - shiny::tags$br(), - shiny::tags$div( - shiny::tags$strong("Select X-Axis Variable"), - teal.picks::picks_ui(ns("var_x"), var_x) - ), - shiny::tags$div( - shiny::tags$strong("Select Y-Axis Variable"), - teal.picks::picks_ui(ns("var_y"), var_y) - ) - ) - ) - ) -} - -srv_example_module <- function(id, data, var_x, var_y, decorators) { - checkmate::assert_string(id) - checkmate::assert_class(data, "reactive") - - select_decorators <- getFromNamespace("select_decorators", "teal") # import from teal internal functions - shiny::moduleServer(id, function(input, output, session) { - selectors <- teal.picks::picks_srv("picks", picks = list(var_x = var_x, var_y = var_y), data = data) - merged <- teal.picks::merge_srv( - "merge_picks", - data = data, - selectors = selectors, - output_name = "anl", - join_fun = "dplyr::inner_join" - ) - # Data preparation - validated_q <- shiny::reactive({ - shiny::validate( - teal::need_input( - inputId = "var_x-variables-selected", - condition = length(selectors$var_x()$variables$selected) > 0, - message = "X-Axis Variable must be selected" - ), - teal::need_input( - inputId = "var_y-variables-selected", - condition = length(selectors$var_y()$variables$selected) > 0, - message = "Y-Axis Variable must be selected" - ) - ) - shiny::validate( - teal::need_input( - inputId = c("var_x-variables-selected", "var_y-variables-selected"), - condition = !any(selectors$var_x()$variables$selected %in% selectors$var_y()$variables$selected), - message = "X-axis variable and Y-axis variable must be different" - ) - ) - q <- merged$data() - teal.reporter::teal_card(q) <- c(teal.reporter::teal_card(q), "## Module's output") - q - }) - - # Generate plot inside qenv - qenv_plot <- reactive({ - within(validated_q(), { - plot <- ggplot2::ggplot(anl) + - ggplot2::geom_point(ggplot2::aes(x = env_var_x, y = env_var_y)) - }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) - }) - decorated_plot <- teal::srv_transform_teal_data( - "decorator_table", - qenv_plot, - select_decorators(decorators, "plot"), - expr = quote(plot) - ) - - qenv_table <- reactive({ - within(validated_q(), { - table <- gtsummary::tbl_summary(anl, by = env_var_x, missing = "no") - }, env_var_x = as.name(merged$variables()$var_x), env_var_y = as.name(merged$variables()$var_y)) - }) - decorated_table <- teal::srv_transform_teal_data( - "decorator_table", - qenv_table, - select_decorators(decorators, "table"), - expr = quote(table) - ) - - # Output rendering: use ggplot2 for visualizations - output$plot <- shiny::renderPlot(decorated_plot()[["plot"]]) - output$table <- gt::render_gt(expr = gtsummary::as_gt(decorated_table()[["table"]])) - # Return reactive - - reactive(c(decorated_plot(), decorated_table())) - }) -} - -tm_example_module <- function( - label = "Example Module", - var_x = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 1L)), - var_y = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(is.numeric, selected = 2L)), - decorators = list(), - transformators = list() -) { - checkmate::assert_string(label) - checkmate::assert_class(var_x, "picks") - checkmate::assert_class(var_y, "picks") - checkmate::assert_list(transformators, types = "teal_transform_module") - args <- list(var_x = var_x, var_y = var_y, decorators = decorators) - teal::module( - label = label, - server = srv_example_module, - ui = ui_example_module, - ui_args = args[names(args) %in% names(formals(ui_example_module))], - server_args = args[names(args) %in% names(formals(srv_example_module))], - transformators = transformators - ) -} -``` +Avoid importing package functions via roxygen2 (`#' @import pkg`)tags in +favor of explicit namespacing for clarity when appropriate. When needed +prefer specific imports over full package imports. ### Code Style for Modules -- **Use tidyverse style**: Write clear, readable code using dplyr, - ggplot2 patterns -- **Use maggritr pipes in reproducible execution**: For code executed +- **Use `tidyverse` style**: Write clear, readable code using `dplyr`, + `ggplot2` patterns +- **Use `magrittr` pipes in reproducible execution**: For code executed for `teal_data`/`qenv` data objects with `eval_code()` and `within()` -- **Prefer ggplot2**: For all visualizations over base R plotting -- **Use gt and gtsummary**: For statistical tables and summaries +- **Use crane and gtsummary**: For statistical tables and summaries - **Error handling**: Implement proper validation using `checkmate` and `shiny::validate(teal::need_input(...))` -``` r -plot_data <- data %>% - dplyr::filter(!is.na(variable)) %>% - dplyr::group_by(category) %>% - dplyr::summarise( - mean_value = mean(value), - n = dplyr::n(), - .groups = "drop" - ) - -ggplot2::ggplot(plot_data, ggplot2::aes(x = category, y = mean_value)) + - ggplot2::geom_col(fill = "steelblue") + - ggplot2::labs( - title = "Mean Values by Category", - x = "Category", - y = "Mean Value" - ) + - ggplot2::theme_minimal() -``` - ## Testing Framework ### Testing Philosophy @@ -385,83 +154,17 @@ ggplot2::ggplot(plot_data, ggplot2::aes(x = category, y = mean_value)) + - **Integration over units**: Test realistic usage patterns - **Test Dependencies**.: Add `testthat::skip_if_not_installed(package_name)` only for dependencies - in SUGGESTS or related to tests cases - -### Test Structure - -Follow the established patterns from `test-module_teal.R`: - -``` r -testthat::test_that("function_name works with valid inputs", { - # Setup - test_data <- data.frame(x = 1:10, y = rnorm(10)) - - # Execution - result <- function_name(test_data) - - # Verification - one expectation per test preferably - testthat::expect_s3_class(result, "data.frame") -}) - -testthat::test_that("function_name handles edge cases", { - # Test empty input - testthat::expect_error( - function_name(data.frame()), - "Input data cannot be empty" - ) -}) - -testthat::test_that("function_name validates input types", { - # Test invalid input type - testthat::expect_error( - function_name("not a data frame"), - class = "checkmate_error" - ) -}) -``` + in `Suggests` or related to tests cases ### Shiny Module Testing - **Server functions**: Test with `shiny::testServer()` - **UI functions**: Test basic usage with regular testing (class checks, error generation, snapshots, regexp search). Test UI scenarios and - interactions with `TealAppDriver` (based on `shinytest2::AppDriver`) - for integration testing + interactions with `teal:::TealAppDriver` (based on + `shinytest2::AppDriver`) for integration testing - **Reactive behavior**: Test reactive chains and side effects -``` r -testthat::test_that("srv_my_module processes data correctly", { - # Test server logic - shiny::testServer( - app = srv_my_module, - args = list( - data = reactive(test_data), - filter_panel_api = NULL - ), - expr = { - # Test reactive computations - result <- processed_data() - testthat::expect_s3_class(result, "teal_data") - } - ) -}) - -testthat::test_that("my_module UI renders correctly", { - # Integration test with TealAppDriver - app <- init( - data = teal_data(mtcars = mtcars), - modules = my_module() - ) - - driver <- TealAppDriver$new(app) - withr::defer(driver$stop()) - driver$navigate_teal_tab("My Module") - - # Test UI elements are present - driver$expect_visible("#plot") -}) -``` - ### Test Organization and Naming - **One test file per R file**: `test-module_example.R` for @@ -469,8 +172,8 @@ testthat::test_that("my_module UI renders correctly", { - **Descriptive test names**: Clearly describe what is being tested - **End to end test names**: `test-shinytest2-module_example.R` for `module_example.R` -- **Logical grouping**: Group related tests using `describe()` when - beneficial +- **Logical grouping**: Group related tests using `describe()` and + individual tests with `it()` when beneficial - **Test data**: Create minimal test datasets, avoid external dependencies @@ -478,63 +181,33 @@ testthat::test_that("my_module UI renders correctly", { ### Package Documentation -- **README.md**: Clear overview, installation, basic usage examples +- **`README.md`**: Clear overview, installation, basic usage examples - **Vignettes**: Comprehensive guides for complex functionality -- **Function documentation**: All exported functions must have roxygen2 - documentation -- **NEWS.md**: Detailed changelog following semantic versioning - -### Website Generation - -Use `_pkgdown.yml` for documentation websites: - -``` yaml -url: https://insightsengineering.github.io/package.name +- **Function documentation**: All exported functions must have + `roxygen2` documentation +- **`NEWS.md`**: Detailed changelog of features, bugs and miscellanea + changes affecting the users -template: - package: nesttemplate +### Package Version Management -reference: - - title: "Main Functions" - contents: - - init - - module - - title: "Helper Functions" - contents: - - starts_with("validate_") -``` - -### Version Management - -Do not change versions on your own. +Do not change versions on your own. There is a CI/CD workflow that +manages the versions automatically on the `main` branch. ## CI/CD and Development Workflow -### GitHub Workflows +### Gitlab Workflows (if package hosted in Gitlab) -Use `r.pkg.template` workflows for consistency: +`.gitlab-ci.yml` reuses CI/CD tasks, such as running all unit tests, +`R CMD check`, code quality checks, style checks and website generation. -- **check.yaml**: R CMD check, unit tests, coverage -- **docs.yaml**: Documentation building and deployment -- **audit.yaml**: Security and dependency auditing -- **pkgdown.yaml**: Website generation +### GitHub Workflows (if package hosted in GitHub) -### Pre-commit Hooks +Use r.pkg.template workflows for consistency: -**Always run pre-commit before committing code**: - -``` bash -pre-commit run --all-files -``` - -Fix any issues that pre-commit reports. The error messages are -informative and will guide you on what needs to be fixed. Pre-commit -automatically checks code style, documentation, linting, and other -quality issues. - -### Dependency Management with Staged Dependencies - -`staged_dependencies.yaml` is an old artifact. Ignore it. +- `check.yaml`: R CMD check, unit tests, coverage +- `docs.yaml`: Documentation building and deployment +- `audit.yaml`: Security and dependency auditing +- `pkgdown.yaml`: Website generation ## Quality Assurance @@ -543,11 +216,11 @@ quality issues. - **Test Coverage**: ≥80% line coverage - **Linting**: No lint violations using configured `.lintr` - **Documentation**: 100% of exports documented -- **Dependencies**: Minimal and justified dependencies only ### Code Review Process -- **Pull Request Reviews**: All changes require review +- **Pull Request Reviews**: All changes require human review and + approval - **Automated Checks**: CI must pass before merging - **Breaking Changes**: Require special consideration and communication - **Documentation Updates**: Must accompany functional changes @@ -562,7 +235,5 @@ quality issues. ## Maintenance Guidelines - **Long-term Support**: Maintain backward compatibility when possible +- **Dependencies**: Minimal and justified dependencies only - **Deprecation**: Use `lifecycle` package for function deprecation - -This guide ensures consistency, quality, and maintainability across the -teal ecosystem while following R community best practices. diff --git a/vignettes/_AGENTS.Rmd b/vignettes/_AGENTS.Rmd index 2d23c434..6f0dd1c9 100644 --- a/vignettes/_AGENTS.Rmd +++ b/vignettes/_AGENTS.Rmd @@ -1,5 +1,13 @@ --- -output: github_document +# This file generates AGENTS.md in the package root. +# It is not part of the package build or the pkgdown site. +# To regenerate it, run from the package root: +# rmarkdown::render("vignettes/_AGENTS.Rmd", output_file = file.path("AGENTS.md"), output_dir = ".") +output: + github_document: + html_preview: false +params: + url: "https://raw.githubusercontent.com/insightsengineering/teal/refs/heads/main/SHARED_AGENTS.md" --- # teal.code R Package Development Guide @@ -7,20 +15,6 @@ output: github_document knitr::opts_chunk$set(echo = TRUE) ``` -```{r render, eval=FALSE, include=FALSE} -rmd_path <- "vignettes/_AGENTS.Rmd" - -rmarkdown::render( - input = rmd_path, - output_format = "github_document", - output_file = "AGENTS.md", - output_dir = ".", - quiet = TRUE -) - -file.remove("AGENTS.html") -``` - ## Package Overview `teal.code` is an R package for storing code and its execution environment in objects called `qenv`. @@ -33,9 +27,11 @@ The teal framework uses Shiny to create reproducible environments for data analy manages code and its execution environment. Related packages include: - `teal.reporter`: Integrates reports with reproducible code in teal applications. Its reporter functions accept `qenv` - objects as inputs. +objects as inputs. - `teal`: Provides the core architecture for teal Shiny applications. Teal modules should use `qenv` objects to ensure - that code changes are recorded and reproducible. +that code changes are recorded and reproducible. +- `teal.data`: Stores all data objects, data relations and transformations of data used in teal applications. The `teal.data` +object is based on the `qenv` and therefore used for code reproducibility. For an introduction to `qenv` and its related functions and methods, see @vignettes/qenv.Rmd. @@ -53,11 +49,11 @@ the function might fail. - When fixing bugs or adding features, avoid changing the core principles of `qenv`. Check whether an issue already exists. If the expected behavior is unclear, request that an issue be created before making changes that could have unexpected consequences. -This package is part of the teal framework. The following configuration applies to all packages within the teal framework: +### Debugging and advanced operations ```{r teal_content, echo=FALSE, results='asis'} # Append common shared instructions from teal framework -common_agents_url <- "https://raw.githubusercontent.com/insightsengineering/teal/refs/heads/main/AGENTS.md" # nolint: line_length_linter. +common_agents_url <- params$url common_agents_path <- tempfile() download.file(url = common_agents_url, destfile = common_agents_path, quiet = TRUE) common_agents_shared <- readLines(common_agents_path) From aaa259c56b8b30370b52d088804a0b1899d12fed Mon Sep 17 00:00:00 2001 From: osenan Date: Wed, 30 Sep 2026 13:53:00 +0200 Subject: [PATCH 12/20] docs: include information about debugging qenvs --- AGENTS.md | 18 +++++++++++++++++- vignettes/_AGENTS.Rmd | 14 +++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bfdaa6a7..6b70f111 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,6 +39,9 @@ For an introduction to `qenv` and its related functions and methods, see appropriate comment to link side effects to objects - For a complex `qenv`, consider dividing the work into smaller `qenv` objects that encapsulate distinct logical steps. +- In the line of the previous comment, `qenv` can be combined with + method `c.qenv`. Be careful that they cannot have elements with the + same name and about the combined order of code execution. - For a long or complex `qenv`, choose between `eval_code()` and `within()` based on: - the number of external variables added to the `qenv`; a larger @@ -53,7 +56,20 @@ For an introduction to `qenv` and its related functions and methods, see expected behavior is unclear, request that an issue be created before making changes that could have unexpected consequences. -### +### Debugging `qenv` + +When debugging teal apps, it is very common that the error happens +within the execution of a `qenv`. It is important to know how to debug +`qenv`. Firstly, by default when there is an error within a qenv its +automatically handled, and a `qenv.error` class object will be returned. +That will prevent the teal app to immediately crash and will display an +useful error message to the user. Once identified a problematic `qenv`, +the guidelines to debug it are: - As the `qenv` does not print messages +and warnings to the R console, so explore them with functions +`get_messages` and `get_warnings`. - Access individual elements of the +`qenv` with `get_var[[qenv, "my_var"]]` to inspect internal values. - +Use function `get_code` to extract specifically how an element is +created. That will help in identifying errors. This package is part of the teal framework. The following configuration applies to all packages within the teal framework. diff --git a/vignettes/_AGENTS.Rmd b/vignettes/_AGENTS.Rmd index 6f0dd1c9..097456f6 100644 --- a/vignettes/_AGENTS.Rmd +++ b/vignettes/_AGENTS.Rmd @@ -41,6 +41,8 @@ For an introduction to `qenv` and its related functions and methods, see @vignet - For simple `qenv` objects, favor `within()`, as it supports more readable code. - When creating a `qenv` that uses functions with side effects, such as `runif()`, `library` or `options` always use `eval_code()`. Use the appropriate comment to link side effects to objects - For a complex `qenv`, consider dividing the work into smaller `qenv` objects that encapsulate distinct logical steps. +- In the line of the previous comment, `qenv` can be combined with method `c.qenv`. Be careful that they cannot have elements with +the same name and about the combined order of code execution. - For a long or complex `qenv`, choose between `eval_code()` and `within()` based on: - the number of external variables added to the `qenv`; a larger number favors `eval_code()`; - how often the same external variable is used; greater repetition favors `within()`. @@ -49,7 +51,17 @@ the function might fail. - When fixing bugs or adding features, avoid changing the core principles of `qenv`. Check whether an issue already exists. If the expected behavior is unclear, request that an issue be created before making changes that could have unexpected consequences. -### Debugging and advanced operations +### Debugging `qenv` + +When debugging teal apps, it is very common that the error happens within the execution of a `qenv`. It is important +to know how to debug `qenv`. +Firstly, by default when there is an error within a qenv its automatically handled, and a +`qenv.error` class object will be returned. That will prevent the teal app to immediately crash and will display +an useful error message to the user. +Once identified a problematic `qenv`, the guidelines to debug it are: +- As the `qenv` does not print messages and warnings to the R console, so explore them with functions `get_messages` and `get_warnings`. +- Access individual elements of the `qenv` with `get_var[[qenv, "my_var"]]` to inspect internal values. +- Use function `get_code` to extract specifically how an element is created. That will help in identifying errors. ```{r teal_content, echo=FALSE, results='asis'} # Append common shared instructions from teal framework From 103a690c64d2980e6804c6bede4a8c11cc78dcda Mon Sep 17 00:00:00 2001 From: osenan Date: Thu, 1 Oct 2026 07:33:24 +0200 Subject: [PATCH 13/20] docs: improve sections on related packages and debugging --- AGENTS.md | 51 ++++++++++++++++++++++++++----------------- vignettes/_AGENTS.Rmd | 39 ++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6b70f111..43290c2d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,15 +16,16 @@ The teal framework uses Shiny to create reproducible environments for data analysis. Within this framework, `teal.code` manages code and its execution environment. Related packages include: -- `teal.reporter`: Integrates reports with reproducible code in teal - applications. Its reporter functions accept `qenv` objects as inputs. -- `teal`: Provides the core architecture for teal Shiny applications. - Teal modules should use `qenv` objects to ensure that code changes are - recorded and reproducible. - `teal.data`: Stores all data objects, data relations and - transformations of data used in teal applications. The `teal.data` - object is based on the `qenv` and therefore used for code - reproducibility. + transformations of data used in teal applications, in an object called + `teal_data`. The `teal_data` object inherits from the `qenv` and + therefore shares all properties about rcode reproducibility. +- `teal`: Provides the core architecture for teal Shiny applications. + Teal modules should use `teal_data` objects to ensure that code + changes are recorded and reproducible. +- `teal.reporter`: Integrates reports with reproducible code in teal + applications. Its reporter functions accept `teal_data` objects as + inputs and extend this class. For an introduction to `qenv` and its related functions and methods, see @vignettes/qenv.Rmd. @@ -58,18 +59,28 @@ For an introduction to `qenv` and its related functions and methods, see ### Debugging `qenv` -When debugging teal apps, it is very common that the error happens -within the execution of a `qenv`. It is important to know how to debug -`qenv`. Firstly, by default when there is an error within a qenv its -automatically handled, and a `qenv.error` class object will be returned. -That will prevent the teal app to immediately crash and will display an -useful error message to the user. Once identified a problematic `qenv`, -the guidelines to debug it are: - As the `qenv` does not print messages -and warnings to the R console, so explore them with functions -`get_messages` and `get_warnings`. - Access individual elements of the -`qenv` with `get_var[[qenv, "my_var"]]` to inspect internal values. - -Use function `get_code` to extract specifically how an element is -created. That will help in identifying errors. +Errors in teal applications commonly occur while a `qenv` is being +evaluated. By default, `qenv` handles these errors and returns an object +of class `qenv.error`. This prevents the teal application from crashing +immediately and provides the user with a helpful error message. + +After identifying the problematic `qenv`, use the following steps to +investigate it: + +- Use the `qenv.error` message to identify the line of code that + triggered the error. +- If that line alone does not explain the error, inspect the preceding + code. If necessary, create a temporary version of the `qenv` + containing only the preceding calls, or modify the line that triggered + the error. +- Inspect values stored in the `qenv` using `qenv[["my_value"]]` to help + identify the source of the error. +- Because messages and warnings produced by a `qenv` are not printed to + the R console, inspect them using `get_messages()` and + `get_warnings()`. +- The same techniques for inspecting values, messages, and warnings can + be used when a `qenv` produces incorrect results without raising an + error. This package is part of the teal framework. The following configuration applies to all packages within the teal framework. diff --git a/vignettes/_AGENTS.Rmd b/vignettes/_AGENTS.Rmd index 097456f6..f05a5136 100644 --- a/vignettes/_AGENTS.Rmd +++ b/vignettes/_AGENTS.Rmd @@ -26,12 +26,16 @@ providing full control over the stored code and environment. These objects also The teal framework uses Shiny to create reproducible environments for data analysis. Within this framework, `teal.code` manages code and its execution environment. Related packages include: -- `teal.reporter`: Integrates reports with reproducible code in teal applications. Its reporter functions accept `qenv` -objects as inputs. -- `teal`: Provides the core architecture for teal Shiny applications. Teal modules should use `qenv` objects to ensure -that code changes are recorded and reproducible. -- `teal.data`: Stores all data objects, data relations and transformations of data used in teal applications. The `teal.data` -object is based on the `qenv` and therefore used for code reproducibility. +- `teal.data`: Stores all data objects, data relations and + transformations of data used in teal applications, in an object called + `teal_data`. The `teal_data` object inherits from the `qenv` and therefore + shares all properties about rcode reproducibility. +- `teal`: Provides the core architecture for teal Shiny applications. + Teal modules should use `teal_data` objects to ensure that code changes are + recorded and reproducible. +- `teal.reporter`: Integrates reports with reproducible code in teal + applications. Its reporter functions accept `teal_data` objects as inputs and + extend this class. For an introduction to `qenv` and its related functions and methods, see @vignettes/qenv.Rmd. @@ -53,15 +57,20 @@ unexpected consequences. ### Debugging `qenv` -When debugging teal apps, it is very common that the error happens within the execution of a `qenv`. It is important -to know how to debug `qenv`. -Firstly, by default when there is an error within a qenv its automatically handled, and a -`qenv.error` class object will be returned. That will prevent the teal app to immediately crash and will display -an useful error message to the user. -Once identified a problematic `qenv`, the guidelines to debug it are: -- As the `qenv` does not print messages and warnings to the R console, so explore them with functions `get_messages` and `get_warnings`. -- Access individual elements of the `qenv` with `get_var[[qenv, "my_var"]]` to inspect internal values. -- Use function `get_code` to extract specifically how an element is created. That will help in identifying errors. +Errors in teal applications commonly occur while a `qenv` is being evaluated. By default, `qenv` handles these errors +and returns an object of class `qenv.error`. This prevents the teal application from crashing immediately and provides +the user with a helpful error message. + +After identifying the problematic `qenv`, use the following steps to investigate it: + +- Use the `qenv.error` message to identify the line of code that triggered the error. +- If that line alone does not explain the error, inspect the preceding code. If necessary, create a temporary version + of the `qenv` containing only the preceding calls, or modify the line that triggered the error. +- Inspect values stored in the `qenv` using `qenv[["my_value"]]` to help identify the source of the error. +- Because messages and warnings produced by a `qenv` are not printed to the R console, inspect them using + `get_messages()` and `get_warnings()`. +- The same techniques for inspecting values, messages, and warnings can be used when a `qenv` produces incorrect + results without raising an error. ```{r teal_content, echo=FALSE, results='asis'} # Append common shared instructions from teal framework From 37691bbc257c1b01d9f20e7388997666531d6db3 Mon Sep 17 00:00:00 2001 From: osenan Date: Thu, 1 Oct 2026 07:38:02 +0200 Subject: [PATCH 14/20] chore: fix typo --- AGENTS.md | 2 +- vignettes/_AGENTS.Rmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 43290c2d..2550adef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ execution environment. Related packages include: - `teal.data`: Stores all data objects, data relations and transformations of data used in teal applications, in an object called `teal_data`. The `teal_data` object inherits from the `qenv` and - therefore shares all properties about rcode reproducibility. + therefore shares all properties about code reproducibility. - `teal`: Provides the core architecture for teal Shiny applications. Teal modules should use `teal_data` objects to ensure that code changes are recorded and reproducible. diff --git a/vignettes/_AGENTS.Rmd b/vignettes/_AGENTS.Rmd index f05a5136..818cefc4 100644 --- a/vignettes/_AGENTS.Rmd +++ b/vignettes/_AGENTS.Rmd @@ -29,7 +29,7 @@ manages code and its execution environment. Related packages include: - `teal.data`: Stores all data objects, data relations and transformations of data used in teal applications, in an object called `teal_data`. The `teal_data` object inherits from the `qenv` and therefore - shares all properties about rcode reproducibility. + shares all properties about code reproducibility. - `teal`: Provides the core architecture for teal Shiny applications. Teal modules should use `teal_data` objects to ensure that code changes are recorded and reproducible. From f1b95257d9abc1afc632c0a74b3f943e0485a524 Mon Sep 17 00:00:00 2001 From: osenan Date: Fri, 2 Oct 2026 13:19:56 +0200 Subject: [PATCH 15/20] refactor: generalize removal of optional sections --- AGENTS.md | 26 +++++--------------------- vignettes/_AGENTS.Rmd | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2550adef..298ca57c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,6 +82,8 @@ investigate it: be used when a `qenv` produces incorrect results without raising an error. + + This package is part of the teal framework. The following configuration applies to all packages within the teal framework. @@ -93,15 +95,14 @@ Follow the standard R package structure with teal-specific conventions: ``` text package_name/ -├── .gitlab-ci.yml # CI/CD workflows (if package hosted in Gitlab) -├── .github # CI/CD workflows (if package hosted in GitHub) +├── .github # CI/CD workflows ├── R/ # R source code ├── tests/testthat/ # Unit tests using testthat ├── vignettes/ # Long-form documentation ├── inst/ # Package assets ├── AGENTS.md # Development guide for AI agents (this file) ├── DESCRIPTION # Package metadata -├── NAMESPACE # Exports and imports automa +├── NAMESPACE # Exports and imports ├── NEWS.md # Change log ├── README.md # Package overview ├── _pkgdown.yml # Documentation website config @@ -158,16 +159,6 @@ Avoid importing package functions via roxygen2 (`#' @import pkg`)tags in favor of explicit namespacing for clarity when appropriate. When needed prefer specific imports over full package imports. -### Code Style for Modules - -- **Use `tidyverse` style**: Write clear, readable code using `dplyr`, - `ggplot2` patterns -- **Use `magrittr` pipes in reproducible execution**: For code executed - for `teal_data`/`qenv` data objects with `eval_code()` and `within()` -- **Use crane and gtsummary**: For statistical tables and summaries -- **Error handling**: Implement proper validation using `checkmate` and - `shiny::validate(teal::need_input(...))` - ## Testing Framework ### Testing Philosophy @@ -222,14 +213,7 @@ manages the versions automatically on the `main` branch. ## CI/CD and Development Workflow -### Gitlab Workflows (if package hosted in Gitlab) - -`.gitlab-ci.yml` reuses CI/CD tasks, such as running all unit tests, -`R CMD check`, code quality checks, style checks and website generation. - -### GitHub Workflows (if package hosted in GitHub) - -Use r.pkg.template workflows for consistency: +Prefer to reuse templates from r.pkg.template. Main checks in place are: - `check.yaml`: R CMD check, unit tests, coverage - `docs.yaml`: Documentation building and deployment diff --git a/vignettes/_AGENTS.Rmd b/vignettes/_AGENTS.Rmd index 818cefc4..9b8120a1 100644 --- a/vignettes/_AGENTS.Rmd +++ b/vignettes/_AGENTS.Rmd @@ -21,7 +21,7 @@ knitr::opts_chunk$set(echo = TRUE) It supports reproducible code execution in teal Shiny applications. Developers can inspect and modify `qenv` objects, providing full control over the stored code and environment. These objects also capture warnings and errors automatically. -## Development Context +## Development Context The teal framework uses Shiny to create reproducible environments for data analysis. Within this framework, `teal.code` manages code and its execution environment. Related packages include: @@ -78,8 +78,20 @@ common_agents_url <- params$url common_agents_path <- tempfile() download.file(url = common_agents_url, destfile = common_agents_path, quiet = TRUE) common_agents_shared <- readLines(common_agents_path) -# remove a heading line only intented to aovid a markdown lint error -line_to_avoid_lintr <- grep("^# ", common_agents_shared) -common_agents_shared <- common_agents_shared[-1 * line_to_avoid_lintr] -cat(common_agents_shared, sep = "\n") +# remove optional sections +remove_sections <- c("