From 98075483a0069f5e7dd4c43010d9a61befbb92a3 Mon Sep 17 00:00:00 2001 From: Douglas Ezra Morrison Date: Mon, 25 May 2026 22:15:39 -0700 Subject: [PATCH 1/3] feat: add Quarto review prompt, skip-cp-setup label, and render QA guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bundles four AI-config improvements surveyed from sibling repos: - .github/prompts/quarto-review.prompt.md — a reusable Copilot review prompt (Quarto-first checklist, severity-ordered output). From rme/bcs. - copilot-setup-steps.yml — a `skip-cp-setup` PR label that short-circuits the heavy install (checkout only) for metadata/workflow-only PRs, with labeled/unlabeled triggers and pull-requests: read. From rpt. - copilot-instructions.md — a "Quality Assurance / Testing Renders" section (render, check _site/ output, review preview, fix before requesting review). From qbt, adapted docs/ -> _site/. - CLAUDE.md — render only the touched page while iterating, not the whole site (the /render command is for the full build). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/copilot-instructions.md | 16 +++++++++ .github/prompts/quarto-review.prompt.md | 44 +++++++++++++++++++++++ .github/workflows/copilot-setup-steps.yml | 41 +++++++++++++++++---- CLAUDE.md | 2 +- 4 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 .github/prompts/quarto-review.prompt.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ec223d7..8e613b0 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -77,3 +77,19 @@ ggplot(mtcars, aes(x = wt, y = mpg)) + labs(x = "Weight (1000 lbs)", y = "Miles per Gallon") ``` ```` + +## Quality Assurance + +### Testing Renders + +Before requesting review or marking work as complete: + +- **Test your changes locally** by running `quarto render` (or rendering only the touched page with `quarto render .qmd --to html` while iterating). +- **Check the rendered output** in `_site/` to verify: + - All content displays correctly + - No broken links or missing images + - Formatting is as expected +- **Review the PR preview** at the preview URL to confirm everything works in the deployed version. +- **Fix any rendering issues** before requesting review. + +This ensures reviewers see working, polished output rather than discovering basic rendering problems. diff --git a/.github/prompts/quarto-review.prompt.md b/.github/prompts/quarto-review.prompt.md new file mode 100644 index 0000000..fbb1686 --- /dev/null +++ b/.github/prompts/quarto-review.prompt.md @@ -0,0 +1,44 @@ +--- +mode: agent +description: Quarto-first PR review for this repository — content, rendering correctness, links, and validation gaps. Use when preparing or checking a PR that touches pages, rendering logic, or workflow files. +--- + +Review the requested changes as a Quarto-first code review for this repository. + +Scope: + +- Prioritize changed `.qmd`, `.R`, `.Rmd`, `.yml`, and `.yaml` files. +- Treat files under `_extensions/` as vendored third-party code. Read them for + context if needed, but do not flag or request changes there. +- Focus on behavior, rendering correctness, broken links or references, and + validation gaps. + +Review checklist: + +1. Links point to source `.qmd` targets, not rendered `.html` files; cross-refs + (`@fig-`, `@tbl-`, `@sec-`) resolve to defined labels. +2. Lists of three or more items use bullets with a blank line above them. +3. `code-fold: true` is used where the *output* is the point and avoided on + tutorial code; chunk options use `#|` directives, not inline `r, opt = val`. +4. Narrative text does not hard-code computed values — they are computed in a + chunk and referenced with inline R. +5. Citations and attribution are present where adapted content or factual + claims appear, and only sources in `references.bib` are used. +6. No new R package or Quarto extension is added without a clear reason (this is + a template — every dependency lands in every downstream book). +7. No generated files are edited (`README.md` from `README.Rmd`; `_site/`, + `_freeze/`, `.quarto/`), and spell/link-check failures are fixed at the + source (wordlist or content), not suppressed. +8. The author ran the required local validation: render of the touched page, + `lintr`, and `spelling::spell_check_package()`. + +Output format: + +- Findings first, ordered by severity, with file references. +- Then open questions or assumptions. +- Then a short change summary only if useful. +- If there are no findings, say so explicitly and note any residual testing gaps. + +Base the review on [copilot-instructions.md](../copilot-instructions.md) and +[CLAUDE.md](../../CLAUDE.md), plus any path-scoped rules under +`.github/instructions/` that apply to the changed files. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 1608577..4d19cb0 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -12,13 +12,18 @@ name: "Copilot Setup Steps" # Automatically run the setup steps when they are changed to allow for easy validation, -# and allow manual testing through the repository's "Actions" tab +# and allow manual testing through the repository's "Actions" tab. +# +# Adding the `skip-cp-setup` label to a PR short-circuits the heavy install +# (checkout only), so metadata- or workflow-only PRs don't pay the full setup +# cost. The `labeled`/`unlabeled` triggers let toggling the label re-run. on: workflow_dispatch: push: paths: - .github/workflows/copilot-setup-steps.yml pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] paths: - .github/workflows/copilot-setup-steps.yml @@ -31,18 +36,36 @@ jobs: # Copilot will be given its own token for its operations. permissions: contents: read - + pull-requests: read # read PR labels for the skip-cp-setup check + # Timeout after 55 minutes (max is 59 for copilot-setup-steps) timeout-minutes: 55 - + steps: + # Skip the heavy install when a PR carries the `skip-cp-setup` label + # (e.g. metadata- or workflow-only PRs). Non-PR events always run setup. + - name: Check if setup should be skipped + id: check_label + shell: bash + run: | + if [[ '${{ github.event_name }}' != 'pull_request' ]]; then + echo "Not a pull request, running full setup" + echo "skip=false" >> "$GITHUB_OUTPUT" + elif [[ '${{ contains(github.event.pull_request.labels.*.name, 'skip-cp-setup') }}' == 'true' ]]; then + echo "skip-cp-setup label present, skipping setup steps" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip-cp-setup label not present, running full setup" + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + # Checkout code - Copilot will do this automatically if we don't, # but we need it for any repository-specific setup - name: Checkout code uses: actions/checkout@v4 with: submodules: true - + # Install system dependencies needed for this project, including: # # - C/C++ libraries required by R packages (libcurl, libssl, libxml2, etc.) @@ -51,6 +74,7 @@ jobs: # - A computer algebra system (maxima) for symbolic math computations # - Python pip for additional tooling - name: Install system dependencies + if: steps.check_label.outputs.skip != 'true' run: | sudo apt-get update sudo apt-get install -y \ @@ -74,11 +98,13 @@ jobs: # Set up pandoc for documentation - name: Set up Pandoc + if: steps.check_label.outputs.skip != 'true' uses: r-lib/actions/setup-pandoc@v2 - + # Set up R using the standard GitHub Actions setup # Using 'release' to get the latest R version - name: Set up R + if: steps.check_label.outputs.skip != 'true' uses: r-lib/actions/setup-r@v2 with: r-version: 'release' @@ -86,6 +112,7 @@ jobs: # Install R packages needed for linting and Quarto rendering - name: Install R packages + if: steps.check_label.outputs.skip != 'true' run: | install.packages( c("lintr", "rmarkdown"), @@ -95,12 +122,14 @@ jobs: # Set up Quarto - required for rendering the website - name: Set up Quarto + if: steps.check_label.outputs.skip != 'true' uses: quarto-dev/quarto-actions/setup@v2 with: tinytex: true - + # Verify development environment is properly configured - name: Verify development environment + if: steps.check_label.outputs.skip != 'true' run: | echo "=== Development Environment Status ===" diff --git a/CLAUDE.md b/CLAUDE.md index 3500b0d..1d09595 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,7 +33,7 @@ Mirrors [`.github/copilot-instructions.md`](.github/copilot-instructions.md). Ke ## Working in this repo - **Don't edit generated files**: `README.md` is built from `README.Rmd`; `_site/` and `_freeze/` are build outputs. -- **Local preview**: `quarto preview` (live reload). Full build: `quarto render`. +- **Local preview**: `quarto preview` (live reload). Full build: `quarto render`. When verifying a single edited page, render just that page (`quarto render .qmd --to html`) rather than the whole site — the `/render` command is for the full build. - **Submodules**: `macros/` is the only git submodule (see `.gitmodules`). Run `git submodule update --init --recursive` after cloning. - **Spell check**: words go in `inst/WORDLIST` (see `.github/workflows/check-spelling.yaml`). Update the wordlist instead of disabling the check. - **Link check**: tuned in `lychee.toml`; prefer fixing broken links over adding exceptions. From 0baffedc51d52b89d0f1c022c4b91decd2771701 Mon Sep 17 00:00:00 2001 From: Douglas Ezra Morrison Date: Mon, 25 May 2026 22:24:13 -0700 Subject: [PATCH 2/3] fix: address review on prompt + skip-cp-setup (PR #81) - quarto-review.prompt.md: convert checklist item 8's three comma-separated validation steps to a bullet list, per the repo's own bullet-list rule. - copilot-setup-steps.yml: pass the event name and label-contains expression through env vars in the skip check rather than interpolating them into the shell, the GitHub-recommended pattern against expression injection. (The `paths:` filter does not block the skip-cp-setup label: Copilot invokes copilot-setup-steps directly when provisioning its environment, independent of the `on:` triggers, so the label is honored there.) Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/prompts/quarto-review.prompt.md | 6 ++++-- .github/workflows/copilot-setup-steps.yml | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/prompts/quarto-review.prompt.md b/.github/prompts/quarto-review.prompt.md index fbb1686..a3abb8a 100644 --- a/.github/prompts/quarto-review.prompt.md +++ b/.github/prompts/quarto-review.prompt.md @@ -29,8 +29,10 @@ Review checklist: 7. No generated files are edited (`README.md` from `README.Rmd`; `_site/`, `_freeze/`, `.quarto/`), and spell/link-check failures are fixed at the source (wordlist or content), not suppressed. -8. The author ran the required local validation: render of the touched page, - `lintr`, and `spelling::spell_check_package()`. +8. The author ran the required local validation: + - render of the touched page + - `lintr` + - `spelling::spell_check_package()` Output format: diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 4d19cb0..b934111 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -46,12 +46,18 @@ jobs: # (e.g. metadata- or workflow-only PRs). Non-PR events always run setup. - name: Check if setup should be skipped id: check_label + # Pass the expression result through env (GitHub-recommended) rather + # than interpolating it directly into the shell, to avoid the + # expression-injection anti-pattern in code that downstream books copy. + env: + EVENT_NAME: ${{ github.event_name }} + LABELS_CONTAIN_SKIP: ${{ contains(github.event.pull_request.labels.*.name, 'skip-cp-setup') }} shell: bash run: | - if [[ '${{ github.event_name }}' != 'pull_request' ]]; then + if [[ "$EVENT_NAME" != 'pull_request' ]]; then echo "Not a pull request, running full setup" echo "skip=false" >> "$GITHUB_OUTPUT" - elif [[ '${{ contains(github.event.pull_request.labels.*.name, 'skip-cp-setup') }}' == 'true' ]]; then + elif [[ "$LABELS_CONTAIN_SKIP" == 'true' ]]; then echo "skip-cp-setup label present, skipping setup steps" echo "skip=true" >> "$GITHUB_OUTPUT" else From 9e1171470d4a0be5b51b58d85ff501a26563ee24 Mon Sep 17 00:00:00 2001 From: Douglas Ezra Morrison Date: Mon, 25 May 2026 22:31:07 -0700 Subject: [PATCH 3/3] docs: clarify setup-steps dual nature; qualify instructions ref (PR #81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - copilot-setup-steps.yml: document that the workflow has two independent invocation paths — Copilot's agent provisioning runs the job directly (not via `on:`, so `paths:` does not affect it; the skip-cp-setup label applies there) vs. the `on:`-triggered validation CI runs (paths-gated to this file). This explains why the paths filter is correct and pre-empts the recurring "paths blocks the label" misreading. - quarto-review.prompt.md: qualify the `.github/instructions/` reference with "(if present)" since that directory may not exist in a fresh template copy. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/prompts/quarto-review.prompt.md | 2 +- .github/workflows/copilot-setup-steps.yml | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/prompts/quarto-review.prompt.md b/.github/prompts/quarto-review.prompt.md index a3abb8a..4e70dea 100644 --- a/.github/prompts/quarto-review.prompt.md +++ b/.github/prompts/quarto-review.prompt.md @@ -43,4 +43,4 @@ Output format: Base the review on [copilot-instructions.md](../copilot-instructions.md) and [CLAUDE.md](../../CLAUDE.md), plus any path-scoped rules under -`.github/instructions/` that apply to the changed files. +`.github/instructions/` (if present) that apply to the changed files. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index b934111..a7a94e7 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -11,12 +11,18 @@ name: "Copilot Setup Steps" -# Automatically run the setup steps when they are changed to allow for easy validation, -# and allow manual testing through the repository's "Actions" tab. +# This workflow has two independent invocation paths: # -# Adding the `skip-cp-setup` label to a PR short-circuits the heavy install -# (checkout only), so metadata- or workflow-only PRs don't pay the full setup -# cost. The `labeled`/`unlabeled` triggers let toggling the label re-run. +# 1. Copilot agent provisioning: GitHub's Copilot coding agent runs the +# `copilot-setup-steps` job DIRECTLY to build its environment when it +# starts a task. That invocation is NOT governed by the `on:` triggers +# below — so the `paths:` filter does not affect it. The `skip-cp-setup` +# label short-circuits the heavy install (checkout only) in that context, +# so a PR Copilot is working on can opt out of the full setup cost. +# 2. Validation CI: the `on:` triggers below run the workflow as an ordinary +# job, gated to changes of THIS file so we can validate edits to it +# without paying the setup cost on every unrelated PR. The +# `labeled`/`unlabeled` types let toggling the label re-run a validation. on: workflow_dispatch: push: