From 22ce5948cb9d2fe7f85767bb458b65d2545137b7 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Thu, 23 Jul 2026 16:55:20 -0700 Subject: [PATCH 01/29] Add Bioconductor package-dev cross-tool skill, review agent, and knowledge base Build reusable AI-assistant tooling for developing, maintaining, submitting, and reviewing Bioconductor R packages, distilled from the official contributions guide. - knowledge/: 20 task-oriented summaries covering all 33 chapters + appendices A-H, plus an end-to-end submission runbook (workflow.md) and topic router (index.md). Single source of truth; each summary links its canonical chapter and stamps the fetch date. SOURCES.md pins the upstream pkgrevdocs commit for drift detection. - Cross-tool adapters: AGENTS.md (Codex/Cursor/Gemini/Copilot), with GEMINI.md and CLAUDE.md importing it. - Claude Code plugin: skills/bioconductor-package-dev/SKILL.md and agents/bioc-package-review.md, packaged via .claude-plugin/{plugin,marketplace}.json. - templates/: lean package skeletons; scripts/check-submission.R runs the gate. - context/: plan, session state, TODO, and REFRESH.md maintenance runbook. --- .claude-plugin/marketplace.json | 15 ++ .claude-plugin/plugin.json | 11 + AGENTS.md | 58 +++++ CLAUDE.md | 8 + GEMINI.md | 5 + README.md | 97 +++++++ agents/bioc-package-review.md | 70 +++++ context/REFRESH.md | 65 +++++ context/SESSION_STATE.md | 53 ++++ context/TODO.md | 51 ++++ context/plan.md | 244 ++++++++++++++++++ knowledge/01-submissions.md | 87 +++++++ knowledge/SOURCES.md | 59 +++++ knowledge/appendices.md | 64 +++++ knowledge/development/ai-policy.md | 29 +++ .../development/build-check-bioccheck.md | 57 ++++ knowledge/development/compiled-thirdparty.md | 32 +++ knowledge/development/data.md | 50 ++++ knowledge/development/documentation.md | 40 +++ knowledge/development/general-dev.md | 59 +++++ knowledge/development/gitignore.md | 34 +++ knowledge/development/metadata-files.md | 121 +++++++++ knowledge/development/methods-classes.md | 42 +++ knowledge/development/non-software-pkgs.md | 41 +++ knowledge/development/package-name.md | 37 +++ knowledge/development/r-code.md | 62 +++++ knowledge/development/shiny.md | 38 +++ knowledge/development/tests.md | 42 +++ knowledge/index.md | 55 ++++ knowledge/maintenance.md | 187 ++++++++++++++ knowledge/reviewer.md | 46 ++++ knowledge/workflow.md | 81 ++++++ scripts/check-submission.R | 91 +++++++ skills/bioconductor-package-dev/SKILL.md | 78 ++++++ templates/.Rbuildignore | 13 + templates/.gitignore | 22 ++ templates/DESCRIPTION | 29 +++ templates/NEWS.md | 10 + templates/inst/CITATION | 14 + 39 files changed, 2197 insertions(+) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 GEMINI.md create mode 100644 README.md create mode 100644 agents/bioc-package-review.md create mode 100644 context/REFRESH.md create mode 100644 context/SESSION_STATE.md create mode 100644 context/TODO.md create mode 100644 context/plan.md create mode 100644 knowledge/01-submissions.md create mode 100644 knowledge/SOURCES.md create mode 100644 knowledge/appendices.md create mode 100644 knowledge/development/ai-policy.md create mode 100644 knowledge/development/build-check-bioccheck.md create mode 100644 knowledge/development/compiled-thirdparty.md create mode 100644 knowledge/development/data.md create mode 100644 knowledge/development/documentation.md create mode 100644 knowledge/development/general-dev.md create mode 100644 knowledge/development/gitignore.md create mode 100644 knowledge/development/metadata-files.md create mode 100644 knowledge/development/methods-classes.md create mode 100644 knowledge/development/non-software-pkgs.md create mode 100644 knowledge/development/package-name.md create mode 100644 knowledge/development/r-code.md create mode 100644 knowledge/development/shiny.md create mode 100644 knowledge/development/tests.md create mode 100644 knowledge/index.md create mode 100644 knowledge/maintenance.md create mode 100644 knowledge/reviewer.md create mode 100644 knowledge/workflow.md create mode 100755 scripts/check-submission.R create mode 100644 skills/bioconductor-package-dev/SKILL.md create mode 100644 templates/.Rbuildignore create mode 100644 templates/.gitignore create mode 100644 templates/DESCRIPTION create mode 100644 templates/NEWS.md create mode 100644 templates/inst/CITATION diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..75f359b --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,15 @@ +{ + "name": "bioc-package-dev", + "description": "Tooling for developing, maintaining, submitting, and reviewing Bioconductor R packages.", + "owner": { + "name": "ybaeus", + "url": "https://github.com/ybaeus/bioc_package_dev" + }, + "plugins": [ + { + "name": "bioconductor-package-dev", + "source": "./", + "description": "Guidance and review for developing, maintaining, submitting, and reviewing Bioconductor R packages." + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..c27068f --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "bioconductor-package-dev", + "description": "Guidance and review for developing, maintaining, submitting, and reviewing Bioconductor R packages, distilled from the official Bioconductor contributions guide.", + "version": "0.1.0", + "author": { + "name": "ybaeus" + }, + "homepage": "https://github.com/ybaeus/bioc_package_dev", + "repository": "https://github.com/ybaeus/bioc_package_dev", + "license": "Apache-2.0" +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f54ba0e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,58 @@ +# Bioconductor package development - agent instructions + +Cross-tool entrypoint (read natively by Codex, Cursor, Gemini CLI, Copilot, and others). When +the task involves developing, maintaining, submitting, or reviewing a Bioconductor package, +follow the rules here and open the matching file under `knowledge/` for detail. The `knowledge/` +directory is the single source of truth; this file is a short router over it. + +## When this applies +Any work on: an R package intended for Bioconductor; `DESCRIPTION` / `NAMESPACE` / `NEWS` / +`biocViews` / `BiocCheck`; S4 or Bioconductor core classes (e.g. SummarizedExperiment); +vignettes and man pages for a Bioc package; submission to the Bioconductor Contributions +tracker; the Bioconductor git server (git.bioconductor.org). It applies even when the user does +not say "Bioconductor" explicitly but the package clearly targets it. + +## Router +- Full submission path, start to finish: `knowledge/workflow.md` +- Topic map across all chapters: `knowledge/index.md` +- Submission mechanics + package types: `knowledge/01-submissions.md` +- Authoring topics (naming, metadata, docs, data, tests, R code, compiled code, shiny, etc.): + `knowledge/development/` +- Maintenance (git server, versioning, build reports, deprecation): `knowledge/maintenance.md` +- What reviewers check: `knowledge/reviewer.md` +- Appendices (devel Bioc, build options, C/Fortran, etc.): `knowledge/appendices.md` + +## Pre-submission gate (hard requirements for a new package) +Do not tell a user their package is submission-ready unless all hold: +- `R CMD check` clean on current R-devel (no errors, no warnings). +- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; + < 8 GB memory for vignettes/examples/tests. +- `Version: 0.99.0`; `biocViews` present; a vignette and man pages present; valid maintainer + email; not on CRAN; hosted on the GitHub default branch. +Detail: `knowledge/development/build-check-bioccheck.md` and `knowledge/development/general-dev.md`. + +## Version rule +Start `0.99.0`. Scheme `x.y.z`: `y` odd in devel, even in release (max 99); bump `z` by 1 on +every commit; `0.99.z` becomes `1.0.0` at the first Bioconductor release; `x` changed only by +the Bioconductor team. Detail: `knowledge/maintenance.md`. + +## Bioconductor code style (differs from tidyverse) +Use `<-` for assignment, 4-space indentation, 80-column lines; prefer vectorized code; avoid +`1:n` (use `seq_len`/`seq_along`). Detail: `knowledge/development/r-code.md`. + +## Submitting and the git server (short) +Host on the GitHub default branch, then open an issue (title = package name) at +https://github.com/Bioconductor/Contributions/issues/new (Annotation packages: email +packages@bioconductor.org). The Single Package Builder must pass on all platforms. After +acceptance, register an SSH key at BiocCredentials, add `upstream = git.bioconductor.org`, and +push to both remotes; only `devel` and `RELEASE_x_y` branches accept pushes. Full sequence: +`knowledge/workflow.md`. + +## Optional helpers +- `scripts/check-submission.R` runs `R CMD check` + `BiocCheck` and reports pass/fail vs the gate + (needs R + BiocCheck installed). +- `templates/` holds lean skeletons (DESCRIPTION, NEWS.md, .Rbuildignore, .gitignore, + inst/CITATION). For full scaffolding, prefer the `biocthis` package. + +Canonical guide: https://contributions.bioconductor.org (source: github.com/Bioconductor/pkgrevdocs). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a71087b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,8 @@ +# Project instructions + +Bioconductor package-development guidance lives in the cross-tool instructions file. Follow it: + +@AGENTS.md + +For Claude Code specifically, the same guidance is packaged as a skill +(`bioconductor-package-dev`) and a review agent (`bioc-package-review`) - see README.md. diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..f0518b6 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,5 @@ +# Project context + +Bioconductor package-development guidance lives in the cross-tool instructions file. Follow it: + +@AGENTS.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a76de0 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# bioc_package_dev + +Reusable AI-assistant tooling for developing, maintaining, submitting, and reviewing +Bioconductor R packages to the project's official standards. The knowledge is distilled from the +official guide "Bioconductor Packages: Development, Maintenance, and Peer Review" +(https://contributions.bioconductor.org) into portable, task-oriented summaries that any AI +coding assistant - or a human - can follow. + +It ships in two forms from one source of truth: +- A Claude Code plugin: a skill (`bioconductor-package-dev`) plus a review agent + (`bioc-package-review`). +- A cross-tool `AGENTS.md` that Codex, Cursor, Gemini CLI, GitHub Copilot, and other assistants + read natively. + +## Repository layout + +``` +bioc_package_dev/ +├── README.md # this file +├── AGENTS.md # cross-tool entrypoint (Codex/Cursor/Gemini/Copilot) +├── GEMINI.md # imports AGENTS.md (for Gemini CLI) +├── CLAUDE.md # imports AGENTS.md (for Claude Code) +├── LICENSE # Apache-2.0 +├── knowledge/ # single source of truth - portable markdown summaries +│ ├── index.md # topic router across all summaries +│ ├── workflow.md # end-to-end submission runbook +│ ├── 01-submissions.md # ch 1 +│ ├── development/ # ch 2-23 (naming, metadata, docs, data, tests, code, ...) +│ ├── maintenance.md # ch 24-30 +│ ├── reviewer.md # ch 31-33 +│ └── appendices.md # A-H +├── templates/ # lean package skeletons +├── scripts/ +│ └── check-submission.R # runs the pre-submission gate checks +├── .claude-plugin/ # Claude Code plugin + marketplace manifests +├── skills/bioconductor-package-dev/SKILL.md +├── agents/bioc-package-review.md +└── context/ # working docs (session state, plan, TODO) +``` + +## Prerequisites + +For the guidance itself, none - it is markdown. To actually build and check a package you need: +- R (current release, plus R-devel for the final submission check). +- `BiocManager`, and `BiocManager::install("BiocCheck")`. +- Optional: `biocthis` for full package scaffolding; `usethis`, `devtools`, `roxygen2`. + +## Install and use + +### Claude Code (plugin) + +One-command install from this repo's marketplace: + +``` +/plugin marketplace add ybaeus/bioc_package_dev +/plugin install bioconductor-package-dev +``` + +Then, in a package project, the skill triggers automatically on Bioconductor work, or invoke it +with `/bioconductor-package-dev`. For a submission-readiness audit, ask Claude to "review my +package for Bioconductor submission" (runs the `bioc-package-review` agent). + +To try it before installing, clone this repo and run: + +``` +claude --plugin-dir /path/to/bioc_package_dev +``` + +### Codex / Cursor / Gemini CLI / GitHub Copilot (AGENTS.md) + +These tools read `AGENTS.md` natively. Either work inside a clone of this repo, or copy +`AGENTS.md` and the `knowledge/` directory into your package project. Gemini CLI also reads +`GEMINI.md` (which imports `AGENTS.md`). + +### Any tool, or a human (manual) + +Read `knowledge/index.md` to find the topic, or `knowledge/workflow.md` for the full submission +path. Point any assistant at the `knowledge/` directory. + +## Keeping it current + +The summaries are stamped with the date they were generated from the live guide, and +`knowledge/SOURCES.md` pins the exact upstream `pkgrevdocs` commit they came from. Bioconductor +updates the guide roughly twice a year with each release. Follow `context/REFRESH.md`: diff the +current upstream commit against the pinned one, regenerate only the changed chapters, bump +`version` in `.claude-plugin/plugin.json`, and push. Marketplace users then run +`/plugin marketplace update` and `/plugin update bioconductor-package-dev`. The summaries always +link back to the canonical chapter, which is the authority if anything drifts. + +## Attribution and license + +The summaries are derived from the Bioconductor contribution guide, source repository +[Bioconductor/pkgrevdocs](https://github.com/Bioconductor/pkgrevdocs) and the rendered guide at +https://contributions.bioconductor.org. That upstream material belongs to the Bioconductor +project; each summary links to its canonical chapter. This repository's own tooling is released +under Apache-2.0 (see `LICENSE`). "Bioconductor" is a trademark of the Bioconductor project; +this project is not affiliated with or endorsed by Bioconductor. diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md new file mode 100644 index 0000000..85490be --- /dev/null +++ b/agents/bioc-package-review.md @@ -0,0 +1,70 @@ +--- +name: bioc-package-review +description: >- + Audit an R package for Bioconductor submission readiness. Use when the user asks to review, + audit, or check whether their package is ready to submit to Bioconductor, or to run a + pre-submission check. Walks the package against the official pre-submission gate and the + contribution guidelines, then returns a structured blockers/warnings/suggestions report mapped + to specific guide chapters. +model: fable +tools: Read, Grep, Glob, Bash +--- + +# Bioconductor package review agent + +You audit an R package against the Bioconductor contribution standards and report whether it is +submission-ready. You are read-mostly: inspect files and run read-only checks; do not modify the +package. Your knowledge base is the plugin's `knowledge/` directory +(`${CLAUDE_PLUGIN_ROOT}/knowledge/`) - consult `reviewer.md`, `workflow.md`, and the development +chapters and cite chapter numbers in findings. + +## What to inspect +Locate the package root (the directory containing `DESCRIPTION`). Then check: + +Metadata (`knowledge/development/metadata-files.md`, ch 6-12): +- `DESCRIPTION`: `Version: 0.99.0` for a new package; `biocViews` present and valid; + `Authors@R` with a maintainer (`cre`) and valid email; `Title`, `Description`, `License` + present; sane `Imports`/`Depends`/`Suggests` (avoid Depends bloat). +- `NAMESPACE`: explicit exports and `importFrom`; no `import()` of whole large packages without + reason. +- `NEWS`/`NEWS.md`, `README`, `LICENSE`, `inst/CITATION` present and well-formed. + +Documentation (`knowledge/development/documentation.md`, ch 13): +- A vignette under `vignettes/` (evaluated, not a stub); man pages for exported objects, with + runnable examples (flag `dontrun`/`donttest` overuse). + +Code and tests (ch 15-16): +- Unit tests present under `tests/`. +- Bioconductor code style: `<-`, 4-space indent, 80-col; flag `1:n` (prefer `seq_len`/`seq_along`), + `sapply` where `vapply` is safer, `T`/`F`, `<<-`, and `.Internal`/`.Call` misuse. + +Data and size (ch 14, 21): +- No large data files; individual files <= 5 MB; large data belongs in ExperimentHub/AnnotationHub. +- `.gitignore` present; no build artifacts, tarballs, or hidden junk committed. + +Reuse (ch 5): uses standard Bioc classes (SummarizedExperiment, GRanges, etc.) where appropriate +rather than reinventing them. + +## The gate (report each as pass/fail) +- `R CMD check` clean on R-devel (no errors, no warnings). +- `BiocCheck::BiocCheckGitClone()` clean. +- `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; files <= 5 MB; < 8 GB memory. +- `Version: 0.99.0`; `biocViews`, vignette, man pages present; valid maintainer; not on CRAN; + hosted on GitHub default branch. + +If R and BiocCheck are installed, you may run `${CLAUDE_PLUGIN_ROOT}/scripts/check-submission.R` +(or `R CMD check` + `BiocCheck::BiocCheck()` directly) from the package root and parse the output. +If R is unavailable, say so and audit statically from the files, marking the check items as +"not run - R unavailable". + +## Report format +Return a structured report, most severe first: + +- Blockers (must fix before submission): each as `- [file or check] problem. Fix: ... (ch N)`. +- Warnings (likely to draw reviewer requests): same format. +- Suggestions (nice to have): same format. +- Gate summary: a short pass/fail list of the gate items above. + +End with a one-line verdict: "Submission-ready" only if there are no blockers and the gate +passes; otherwise "Not yet - N blockers". diff --git a/context/REFRESH.md b/context/REFRESH.md new file mode 100644 index 0000000..0d2492a --- /dev/null +++ b/context/REFRESH.md @@ -0,0 +1,65 @@ +# Refresh runbook + +How to keep the `knowledge/` summaries in sync with the upstream Bioconductor guide. Run this +when Bioconductor cuts a release (around April and October) or whenever `pkgrevdocs` has notable +changes. Baseline state lives in `knowledge/SOURCES.md`. + +## 1. Detect drift + +Check the upstream commit against the pinned one in `knowledge/SOURCES.md`: + +- Current commit: `https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel` -> field `sha`. +- If it equals the pinned SHA, nothing changed. Stop. +- If it differs, list changed files: + `https://github.com/Bioconductor/pkgrevdocs/compare/...devel` + (Fetch these with the WebFetch tool; local `curl` has no CA bundle - use `curl -k` only for + reachability, not for content.) + +Also compare the guide TOC (`https://contributions.bioconductor.org/index.html`) against the +slug map in `knowledge/SOURCES.md` to catch added, removed, or renamed chapters. A renamed slug +silently breaks the link in the affected summary. + +## 2. Map changes to files + +For each changed chapter slug, look up its target file in the `knowledge/SOURCES.md` map. Only +those files need regenerating. A new chapter needs a new file plus a router entry in +`knowledge/index.md` and a map entry in `knowledge/SOURCES.md`. A removed chapter: delete its +section and fix references. + +## 3. Regenerate the changed summaries + +Re-run the same summary pass used to build these files (prompts and format spec are in +`context/plan.md`, "Content generation"). Use fable-model subagents. Format rules that must hold: +- Plain markdown, no emoji. +- Task-oriented (rules, thresholds, required/forbidden patterns), not a transcription. +- Each file ends with a `Source:` line (canonical chapter URL) and a `Fetched ` stamp. + +If the pre-submission gate numbers, the version rule, or the submission mechanics changed, also +update the inline copies in `AGENTS.md`, `skills/bioconductor-package-dev/SKILL.md`, +`agents/bioc-package-review.md`, and `knowledge/workflow.md` (these repeat the gate on purpose). + +## 4. Update the baseline + +In `knowledge/SOURCES.md` set the pinned commit SHA, commit date, and "Summaries fetched" date +to the new values. + +## 5. Verify + +- `PATH=/opt/homebrew/bin:$PATH claude plugin validate .` (expect: passed). +- Link check: extract `contributions.bioconductor.org/*.html` URLs from `knowledge/` and confirm + each returns 200 (`env -u CURL_CA_BUNDLE curl -k -sI ` locally, or WebFetch). +- Load test: `claude --plugin-dir . -p "..."` and confirm the skill + agent still load and the + changed content is reflected. + +## 6. Release + +- Bump `version` in `.claude-plugin/plugin.json` (plugin users only receive updates on a bump). +- Note the change (new NEWS/CHANGELOG line, or the commit message). +- Commit and push to `github.com/ybaeus/bioc_package_dev`. Marketplace users run + `/plugin marketplace update` then `/plugin update bioconductor-package-dev`. + +## Notes +- The canonical chapter link in each summary is always authoritative; a lagging summary still + points readers to the correct source. +- Environment: R 4.4.0 at /usr/local/bin; `claude` at /opt/homebrew/bin; `gh` is not installed + (use the GitHub REST API via WebFetch); local curl needs `-k`. diff --git a/context/SESSION_STATE.md b/context/SESSION_STATE.md new file mode 100644 index 0000000..39fbc85 --- /dev/null +++ b/context/SESSION_STATE.md @@ -0,0 +1,53 @@ +# Session State - bioc_package_dev + +Last updated: 2026-07-23 + +## Goal +Build reusable Claude tooling for Bioconductor package development in this repo: +a Skill (inline authoring/lifecycle guidance) plus a review Agent (submission-readiness +audit), backed by curated summaries of the official guide "Bioconductor Packages: +Development, Maintenance, and Peer Review" (https://contributions.bioconductor.org, +source repo: Bioconductor/pkgrevdocs). + +## Accomplished +- Environment prepped: + - Installed Node 26.5.0 via Homebrew (/opt/homebrew/bin - NOT on default PATH; prepend it + when running node/npx). + - Found and worked around a broken CURL_CA_BUNDLE env var (points to a missing enterprise + cert, causing curl error 77). Prefix network commands with `env -u CURL_CA_BUNDLE`. + Saved to memory. +- Installed caveman (JuliusBrussee/caveman) as a Claude Code plugin (user scope). Concise- + output skill. Activates next session via /caveman or "caveman mode"; stats via /caveman-stats. +- Wrote the build plan and handed it to Ultraplan for remote refinement. + - Local plan file: /Users/ybae/.claude/plans/i-would-like-to-polymorphic-quasar.md + - Ultraplan session: https://claude.ai/code/session_01GuiAoNksAgYvqYzd14b42h?from=cli + +## In progress +- Ultraplan refinement of the build plan - awaiting the refined plan to teleport back for + approval and implementation. + +## Next steps (once plan approved) +1. Scaffold repo layout: + - .claude/skills/bioconductor-package-dev/SKILL.md plus resources/ (per-chapter summaries + with links to live chapters; NOT vendoring pkgrevdocs) + - .claude/agents/bioc-package-review.md (submission-readiness audit; model: fable) + - Repo-root README.md so others can install and follow. +2. Generate chapter summaries with fable-model subagents (33 chapters plus appendices A-H; + full lifecycle: Submissions / Development / Maintenance / Reviewer). +3. Verify: structure and frontmatter parse, chapter URLs resolve, skill trigger smoke test, + agent smoke test against a dummy package skeleton. + +## Future idea (post bioc_package_dev) +- R to Python converter Skill or Agent: convert a Bioconductor (R) package to Python + (e.g. map Bioc classes like SummarizedExperiment / S4 to Python equivalents such as + AnnData / BiocPy, translate DESCRIPTION to pyproject, roxygen to docstrings, testthat to + pytest). Skill vs agent to be decided later; separate follow-on project, not part of the + current build. + +## Key facts / gotchas +- Node at /opt/homebrew/bin (prepend to PATH). Broken CURL_CA_BUNDLE - use + `env -u CURL_CA_BUNDLE`. +- Repo is otherwise greenfield (only LICENSE, .git, plus this file and memory). +- Decisions locked: both skill and agent; summaries plus links (no pkgrevdocs vendoring); + full lifecycle; content work done with the fable model. +- Doc convention: no emoji in any docs in this repo. diff --git a/context/TODO.md b/context/TODO.md new file mode 100644 index 0000000..a62ef7c --- /dev/null +++ b/context/TODO.md @@ -0,0 +1,51 @@ +# TODO - bioc_package_dev + +Running task list. See context/plan.md for the full approved plan. + +## Build +- [x] Scaffold repo structure + context/ (knowledge/, skills/, agents/, templates/, scripts/, .claude-plugin/) +- [x] Generate knowledge/ chapter summaries (all four parts + appendices) + - [x] index.md (router / chapter->file map) + - [x] workflow.md (end-to-end submission runbook) + - [x] 01-submissions.md (ch 1) + - [x] development/ (ch 2-23) - 14 files + - [x] maintenance.md (ch 24-30) + - [x] reviewer.md (ch 31-33) + - [x] appendices.md (A-H) +- [x] Cross-tool adapters: AGENTS.md, GEMINI.md, CLAUDE.md +- [x] Claude Code plugin: .claude-plugin/plugin.json, marketplace.json, skills/.../SKILL.md, agents/bioc-package-review.md +- [x] templates/ (DESCRIPTION, NEWS.md, .Rbuildignore, .gitignore, inst/CITATION) +- [x] scripts/check-submission.R +- [x] README.md + +## Verify +- [x] claude plugin validate - passed (after adding marketplace description) +- [x] claude --plugin-dir . load test - skill + agent both discovered +- [x] link check: all 44 knowledge/ chapter URLs resolve 200 (curl needs -k locally; no CA bundle) +- [x] skill trigger smoke test - "get my package ready for Bioconductor" surfaced skill, returned gate + version rule +- [x] script smoke test - check-submission.R ran against a dummy package (R 4.4.0), clean pass report, degrades without BiocCheck +- [ ] agent smoke test against a real package - deferred (needs a real package + BiocCheck) +- [x] cross-tool AGENTS.md self-contained (router + gate + version + links) + +## Maintenance +- [x] knowledge/SOURCES.md - slug->file map + pinned pkgrevdocs commit (9b078ea, 2026-07-20) +- [x] context/REFRESH.md - drift-detection + regen runbook +- Refresh trigger: each Bioc release (~April/October) or when pkgrevdocs default branch moves + +## Not yet done (optional / future) +- [ ] git add + commit + push to github.com/ybaeus/bioc_package_dev (not committed yet) +- [ ] Install BiocCheck to exercise the full dynamic gate +- [ ] Community-marketplace submission (claude plugin validate + review pipeline) + +## Confirmed facts +- GitHub repo: ybaeus/bioc_package_dev (install: /plugin marketplace add ybaeus/bioc_package_dev) +- Submission tracker: github.com/Bioconductor/Contributions (open issue, package name as title) +- This repo LICENSE: Apache-2.0 +- pkgrevdocs license: NOASSERTION (no standard license declared) - attribute by link, do not claim a license +- Node at /opt/homebrew/bin; broken CURL_CA_BUNDLE - use env -u CURL_CA_BUNDLE +- claude CLI at /opt/homebrew/bin/claude + +## Deferred +- Community-marketplace submission (claude plugin validate + review pipeline) +- skill-creator eval / description tuning +- Future: R->Python Bioconductor package converter diff --git a/context/plan.md b/context/plan.md new file mode 100644 index 0000000..14ad67c --- /dev/null +++ b/context/plan.md @@ -0,0 +1,244 @@ +# Plan: Bioconductor Package Development — Cross-Tool Skill + Review Agent + +## Context + +`bioc_package_dev` is a greenfield repo (only `LICENSE`, `.git`, `SESSION_STATE.md`) meant to +hold reusable AI-assistant tooling for developing Bioconductor packages to the project's +official standards. Knowledge source: the official guide *"Bioconductor Packages: Development, +Maintenance, and Peer Review"* (https://contributions.bioconductor.org), source repo +`Bioconductor/pkgrevdocs`. + +Full TOC fetched + verified live this session: **33 numbered chapters + appendices A–H** across +four parts — Submissions (ch 1), Development Guidelines (ch 2–23), Maintenance (ch 24–30), +Reviewer Resources (ch 31–33), Appendix A–H. The real submission workflow (Contributions +tracker issue → Single Package Builder webhook → review → BiocCredentials SSH + +git.bioconductor.org dual remotes → release) and the hard numeric gates were verified from +chapters 1, general(3), versionnum(25), and git-version-control(24). + +Intended outcome: a shareable git repo whose Bioconductor knowledge is usable across multiple +AI coding assistants (Claude Code, plus Codex / Cursor / Gemini CLI / Copilot via the AGENTS.md +standard), packaged as a Claude Code plugin for one-command install, with a README so other +researchers can adopt it. + +**Decisions (confirmed this session):** +- Build **both** a Skill (inline authoring/lifecycle guidance) **and** an Agent (isolated + submission-readiness audit). +- pkgrevdocs handled via **curated per-chapter summaries + links to live chapters** — NO + vendoring, no submodule. +- **Cross-tool** is a goal: one portable knowledge base, thin per-tool adapters. +- Distribute as a **Claude Code plugin + marketplace** in this repo; document self-host install + now, note community-marketplace submission as a later step. +- Include a **lean** set of templates and **one** check script (rationale below). +- Full-lifecycle scope. Content generated with the **fable** model. No emoji in any docs. + +## Architecture: one knowledge base, thin adapters + +The chapter summaries + workflow runbook are **plain portable markdown** (`knowledge/`) — the +single source of truth, readable by any tool or human. Each assistant gets a thin adapter that +points into `knowledge/`, so there is no content duplication: + +- **AGENTS.md** (repo root) — the cross-tool entrypoint. Read natively by Codex, Cursor, Gemini + CLI, Copilot, Windsurf, and 20+ others. Carries the lifecycle router + pre-submission gate + + version rules inline (small), and references `knowledge/` for detail. +- **Claude Code plugin** — `skills/bioconductor-package-dev/SKILL.md` + `agents/…`, wrapping the + same `knowledge/` via `${CLAUDE_PLUGIN_ROOT}/knowledge`. Distributed via `marketplace.json`. +- **GEMINI.md** and **CLAUDE.md** — one-line files that import AGENTS.md, for tools that prefer + their own filename (both support imports/references). + +## Repo layout + +``` +bioc_package_dev/ +├── README.md # what it is + per-tool install/usage (NEW) +├── AGENTS.md # cross-tool entrypoint (Codex/Cursor/Gemini/Copilot) (NEW) +├── GEMINI.md # one-line import of AGENTS.md (NEW) +├── CLAUDE.md # one-line import of AGENTS.md (NEW) +├── LICENSE # existing +├── context/ # working docs, not shipped tooling (NEW) +│ ├── SESSION_STATE.md # moved from repo root +│ ├── plan.md # copy of this plan for repo-local reference +│ └── TODO.md # running task list +├── knowledge/ # SINGLE SOURCE OF TRUTH — portable markdown (NEW) +│ ├── index.md # router/TOC across all summaries (chapter→file map) +│ ├── workflow.md # END-TO-END submission runbook (stitches the process) +│ ├── 01-submissions.md # ch 1 (+ submission-overview) +│ ├── development/ +│ │ ├── package-name.md # ch 2 +│ │ ├── general-dev.md # ch 3–4 +│ │ ├── methods-classes.md # ch 5 +│ │ ├── metadata-files.md # ch 6–12 (README/DESCRIPTION/NAMESPACE/NEWS/ +│ │ │ # LICENSE/CITATION/INSTALL) +│ │ ├── documentation.md # ch 13 +│ │ ├── data.md # ch 14 +│ │ ├── tests.md # ch 15 +│ │ ├── r-code.md # ch 16 +│ │ ├── compiled-thirdparty.md # ch 17 +│ │ ├── shiny.md # ch 18 +│ │ ├── ai-policy.md # ch 19 +│ │ ├── non-software-pkgs.md # ch 20 +│ │ ├── gitignore.md # ch 21 +│ │ └── build-check-bioccheck.md # ch 22–23 +│ ├── maintenance.md # ch 24–30 +│ ├── reviewer.md # ch 31–33 +│ └── appendices.md # A–H +├── templates/ # lean scaffolding skeletons (NEW, optional) +│ ├── DESCRIPTION +│ ├── NEWS.md +│ ├── .Rbuildignore +│ ├── .gitignore +│ └── inst/CITATION +├── scripts/ # one check runner (NEW, optional) +│ └── check-submission.R # R CMD check + BiocCheck, pass/fail vs the gate +├── .claude-plugin/ +│ ├── plugin.json # plugin manifest (name, version, author) +│ └── marketplace.json # marketplace listing for /plugin install +├── skills/ +│ └── bioconductor-package-dev/ +│ └── SKILL.md # thin: router + gate; points at ${CLAUDE_PLUGIN_ROOT}/knowledge +└── agents/ + └── bioc-package-review.md # submission-readiness audit subagent (model: fable) +``` + +Note: plugin component dirs (`skills/`, `agents/`) live at **repo root**, NOT inside a +`.claude/` dir — the plugin root is this repo, and only `plugin.json` goes inside +`.claude-plugin/`. Grouping thin chapters per file (metadata ch 6–12, maintenance ch 24–30) +keeps `knowledge/` navigable while preserving progressive disclosure. + +`workflow.md` is the sequential publish runbook — added because per-chapter summaries are +reference, not process. It stitches the real developer path end to end: pre-submission gate → +host on GitHub default branch → open issue on the Contributions tracker → SPB (Single Package +Builder) webhook build must pass all platforms → reviewer iteration (bump z each commit, 2–6 +weeks) → acceptance → register SSH key at BiocCredentials, add `upstream` git.bioconductor.org +remote, dual-remote push → devel vs `RELEASE_x_y` branches (cherry-pick to backport) → ongoing +maintenance. Sources: ch1, ch24, ch25, appendix A. + +## Verified chapter → slug map (for summary generation) + +Fetch each from `https://contributions.bioconductor.org/`: + +- Submissions: submission-overview, bioconductor-package-submissions (ch1) +- Development: develop-overview, package-name(2), general(3), + important-bioconductor-package-development-features(4), reusebioc(5), readme(6), + description(7), namespace(8), news(9), license(10), citation(11), sysdep(12), docs(13), + data(14), tests(15), r-code(16), other-than-Rcode(17), shiny(18), + ai-policy-third-party(19), non-software(20), gitignore(21), build-check-bioccheck(22), + conclusion(23) +- Maintenance: package-maintenance, git-version-control(24), versionnum(25), + troubleshooting-build-report(26), debugging-cc-code(27), deprecation(28), + package-end-of-life-policy(29), branch-rename-faqs(30) +- Reviewer: reviewer-resources-overview, review-expectation(31), reviewtools(32), + review-volunteer-chapter(33) +- Appendix: use-devel(A), advanced-build-options(B), querying-web-resources(C), + c-fortran(D), cmavericks-best-practices(E), man-links(F), booknews(G), references-1(H) + +## Content generation + +Fetch + summarize with **fable**-model subagents in parallel, one batch per part. Each summary +is **task-oriented** (actionable rules, required/forbidden patterns, naming/version +conventions), NOT a transcription. Aim ~40–120 lines each. Every summary ends with a link to +its canonical chapter URL **and a "guide version / fetched" stamp** (staleness marker; refresh +bumps plugin `version`). Network note: prefix curl-based fetches with `env -u CURL_CA_BUNDLE` +(broken cert env var); Node at `/opt/homebrew/bin` if needed. Verify the live submission-tracker +repo name at build time — confirm `github.com/Bioconductor/Contributions` before hard-coding it. + +## Cross-cutting rules (shared by AGENTS.md and SKILL.md) + +Both entrypoints carry these inline (short); detail lives in `knowledge/`: +- **Pre-submission gate** (hard numbers): `R CMD check` clean on R-devel; + `BiocCheck::BiocCheckGitClone()` + `BiocCheck::BiocCheck('new-package'=TRUE)` clean; source + build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; < 8 GB + memory; `Version: 0.99.0`; hosted on GitHub default branch; not on CRAN; `biocViews` + + vignette + man pages present; valid maintainer email. +- **Version rule** (named): start `0.99.0`; `y` odd = devel / even = release (max 99); bump `z` + every commit; `0.99.z` becomes `1.0.0` at first Bioc release. +- **Bioc code style**: `<-` assignment, 4-space indent, 80-col; contrast with tidyverse. + +## SKILL.md design (Claude Code) + +- **Frontmatter `description`**: pushy triggering — fire on Bioconductor/Bioc package work, + `DESCRIPTION`/`NAMESPACE`/`NEWS`/BiocCheck/biocViews, submission prep, S4 / + `SummarizedExperiment`-style classes, even when "Bioconductor" isn't said explicitly. +- **Body**: short lifecycle router (Authoring → Maintenance → Submission/Review) mapping the + task to the right `knowledge/` file, plus the cross-cutting rules above. Detail stays in + `knowledge/`, referenced via `${CLAUDE_PLUGIN_ROOT}/knowledge`. + +## Review agent design (`agents/bioc-package-review.md`) + +- Frontmatter: `name`, `description` (triggers on "audit/review my package for Bioconductor + submission"), `model: fable`, read-mostly toolset (Read/Grep/Glob/Bash). +- Behavior: audit against the **explicit pre-submission gate** above, plus the broader checklist + from `knowledge/reviewer.md` + development chapters. Optionally invoke `scripts/check-submission.R` + when R is available. Return a structured report (blockers / warnings / suggestions) each mapped + to a specific guide chapter. Uses `knowledge/` as its base (no duplicated content). + +## Templates and scripts (lean, optional) + +- `templates/` — small curated skeletons (DESCRIPTION, NEWS.md, .Rbuildignore, .gitignore, + inst/CITATION). README points to **`biocthis`** for full scaffolding rather than reinventing + it. Rationale: high scaffolding value, plain files any tool/human copies, low churn. +- `scripts/check-submission.R` — one runner: `R CMD check` + `BiocCheck::BiocCheck()` then + report pass/fail against the gate. Requires R + BiocCheck installed (documented). Skip a + version-bump helper (trivial; `usethis`/manual). Rationale: operationalizes the gate we + emphasize without a maintenance-heavy toolchain. + +## Distribution (`.claude-plugin/` + README) + +- `plugin.json`: `name` = `bioconductor-package-dev`, `description`, explicit `version` (so + users only pull updates on a bump), `author`, `repository`, `license`. +- `marketplace.json`: lists this plugin so users run + `/plugin marketplace add /bioc_package_dev` then `/plugin install`. +- Self-host now; note `claude plugin validate` + community-marketplace submission as a later + step. Need the GitHub `/` to hard-code the install command (confirm at build). + +## README.md + +Repo-root README: one-line statement of what this repo is for; the repo layout tree; +prerequisites (R, Bioc-devel, `BiocManager`, `BiocCheck`, optional `biocthis`); **per-tool +install/usage** — +- Claude Code: `/plugin marketplace add /bioc_package_dev` → `/plugin install`; or clone + and `--plugin-dir`. +- Codex / Cursor / Gemini CLI / Copilot: clone and rely on `AGENTS.md` (Gemini also `GEMINI.md`). +- Manual fallback: copy `knowledge/` and point any tool at it. +—plus a refresh/versioning note, and attribution + license for pkgrevdocs (verify its license +at build; likely CC-BY). Plain text, no emoji. + +## Verification + +1. **Structure**: `.claude-plugin/plugin.json`, `skills/bioconductor-package-dev/SKILL.md`, + `agents/bioc-package-review.md`, `AGENTS.md`, and `knowledge/*` exist and parse (valid YAML + frontmatter, valid JSON manifests, no broken internal links). Run `claude plugin validate`. +2. **Link check**: every canonical chapter URL in `knowledge/` resolves (HTTP 200). +3. **Claude Code load test**: `claude --plugin-dir .` loads the plugin; skill appears as + `/bioconductor-package-dev:…` and the agent under Custom Agents. +4. **Skill trigger smoke test**: "help me get my R package ready for Bioconductor submission" + surfaces the skill; "write my DESCRIPTION for a Bioc package" routes to + `knowledge/development/metadata-files.md`. +5. **Cross-tool check**: confirm `AGENTS.md` is valid and self-contained enough that a + non-Claude tool pointed at the repo gets the router + gate + links. +6. **Agent smoke test**: invoke `bioc-package-review` against a minimal dummy package skeleton; + confirm a structured report referencing guide chapters. +7. Optional later: skill-creator eval loop to tune the SKILL `description` for reliable + triggering. + +## Deferred + +- Community-marketplace submission (`claude plugin validate` + review pipeline). +- Skill-creator formal eval + description-optimization loop. +- Future idea (separate project): R→Python Bioconductor package converter. + +## References (verified this session) + +Bioconductor guide (canonical knowledge source): +- Guide home + TOC: https://contributions.bioconductor.org/index.html +- Submissions ch1: https://contributions.bioconductor.org/bioconductor-package-submissions.html +- General requirements / gate: https://contributions.bioconductor.org/general.html +- Version numbering: https://contributions.bioconductor.org/versionnum.html +- Git workflow (dual remotes, BiocCredentials): https://contributions.bioconductor.org/git-version-control.html +- Source repo: https://github.com/Bioconductor/pkgrevdocs + +Cross-tool distribution basis: +- Claude Code plugins: https://code.claude.com/docs/en/plugins +- Claude Code skills: https://code.claude.com/docs/en/skills +- AGENTS.md standard (2026 guide): https://codersera.com/blog/agents-md-complete-guide-2026/ +- CLAUDE.md vs AGENTS.md vs GEMINI.md: https://inventivehq.com/blog/claude-md-vs-agents-md-vs-gemini-md +- Gemini CLI GEMINI.md docs: https://geminicli.com/docs/cli/gemini-md/ diff --git a/knowledge/01-submissions.md b/knowledge/01-submissions.md new file mode 100644 index 0000000..412ab82 --- /dev/null +++ b/knowledge/01-submissions.md @@ -0,0 +1,87 @@ +# Covers: Chapter 1 - Bioconductor package submission overview, eligibility, package types, and submission mechanics. + +## Eligibility (must all hold for Software packages) + +- Package must address high-throughput genomic analysis (sequencing, microarrays, + flow cytometry, mass spectrometry, image analysis, or similar biological data). +- Must interoperate with other Bioconductor packages by re-using common + Bioconductor data structures (do not reinvent existing classes). +- Must NOT already exist on CRAN (CRAN and Bioconductor are mutually exclusive). +- All dependencies must be available on CRAN or Bioconductor. +- Must include full documentation and evaluated (runnable) vignettes. +- Must comply with the Bioconductor Package Guidelines. +- Maintainer commits to long-term support via the Bioconductor support site. +- Follow software best practices supporting reproducible research. + +## Package types + +- Software: algorithms, resource access, analysis, visualization. Most common + submission type. Goes through the Contributions issue tracker. +- Experiment Data: curated datasets for examples/vignettes. Typically a single + dataset; for larger files use ExperimentHub. Do NOT open a separate issue - + add the experiment data package to the SAME issue as its software package. +- Annotation: databases mapping identifiers to information; updated every 6 + months. Prefer AnnotationHub when possible. Do NOT use the tracker - instead + email packages@bioconductor.org. +- Workflow: demonstrate a multi-package bioinformatics workflow. No man/, R/, or + data/ directories required. Follow the non-software development section. + +## Submission mechanics (Software / Experiment Data) + +- Host the package in a GitHub repository. +- The package must live on the repository's DEFAULT branch, and that branch must + contain ONLY package code. GitHub Actions / devtools / CI helper files must be + kept on separate (non-default) branches. +- Package name must not conflict (case-insensitive) with any current or past + Bioconductor or CRAN package. The contributor grants Bioconductor rights to + the package name (CRAN-style naming/ownership policy applies). +- Open a NEW issue on the tracker at github.com/Bioconductor/Contributions with + the package name as the issue title; link the GitHub repo in the issue and + follow the tracker README.md guidelines. +- The submitter MUST be listed as the package maintainer in DESCRIPTION + (maintainer == submitter) - used to verify credentials. +- The Single Package Builder webhook auto-builds the package on submission (and + on each subsequent push); the build must PASS on all platforms before review + proceeds. +- Annotation packages: email packages@bioconductor.org instead of opening an + issue on the tracker. + +## Maintainer obligations (ongoing) + +- Follow Bioconductor guidelines: version numbering, coding style, performance, + and memory usage standards. +- Maintain the package with git version control. +- Monitor build reports (weekly to daily) and fix breakages promptly. +- Subscribe to the bioc-devel mailing list. +- Register on the support site and monitor the package's "Watched Tags". +- Respond promptly to bug reports and user questions. +- Keep the maintainer email in DESCRIPTION accurate and reachable. +- Bump the "z" (patch) version number on EVERY commit; without a version bump, + changes will not propagate to the build system. + +## Review timeline and response deadlines (hard numbers) + +- Full review typically takes 2 to 6 weeks. +- Expect progress (submitter updates or reviewer comments) within 2-3 weeks. +- After roughly 3-4 weeks of inactivity, reviewers MAY close the issue; respond + within the 2-3 week window to keep it open. +- Changes pushed to the devel branch appear in builds within about 24-48 hours. + +## Release cycle and post-acceptance + +- Two releases per year (approximately April and October). +- Accepted packages first enter the 'devel' branch. +- Bug fixes allowed in both devel and release branches; NEW features restricted + to the devel branch only. +- Annotation packages are updated every 6 months. +- On successful build, a landing page is auto-created and the package becomes + installable via BiocManager::install() (devel users first). + +## Getting help + +- General maintainer help: maintainer@bioconductor.org and the bioc-devel list. +- Topics: S4 class design, implementation guidance, code structure, + documentation review. + +Source: https://contributions.bioconductor.org/bioconductor-package-submissions.html (and overview: https://contributions.bioconductor.org/submission-overview.html) +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/SOURCES.md b/knowledge/SOURCES.md new file mode 100644 index 0000000..5f2b5e8 --- /dev/null +++ b/knowledge/SOURCES.md @@ -0,0 +1,59 @@ +# Sources and refresh baseline + +This file records where each summary came from and the exact upstream state it was generated +against. Use it to detect drift and to refresh only what changed. See `context/REFRESH.md` for +the procedure. + +## Upstream baseline (update on every refresh) + +- Source repository: https://github.com/Bioconductor/pkgrevdocs +- Default branch: `devel` +- Pinned commit (last summarized from): `9b078ea2a0ec05274be83cb12ea75473c6d0c808` +- Commit date: 2026-07-20 +- Summaries fetched: 2026-07-23 +- Rendered guide: https://contributions.bioconductor.org + +To find the current upstream commit: +`https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel` (field `sha`). Compare the +`sha` to the pinned commit above. If they differ, list changed chapter files with the GitHub +compare API: `https://github.com/Bioconductor/pkgrevdocs/compare/...devel` + +## Chapter slug -> knowledge file map + +Every page is `https://contributions.bioconductor.org/.html`. + +Submissions: +- submission-overview, bioconductor-package-submissions (ch1) -> `01-submissions.md` + +Development (ch 2-23) -> `development/`: +- package-name (ch2) -> `development/package-name.md` +- general (ch3), important-bioconductor-package-development-features (ch4) -> `development/general-dev.md` +- reusebioc (ch5) -> `development/methods-classes.md` +- readme (ch6), description (ch7), namespace (ch8), news (ch9), license (ch10), citation (ch11), + sysdep (ch12) -> `development/metadata-files.md` +- docs (ch13) -> `development/documentation.md` +- data (ch14) -> `development/data.md` +- tests (ch15) -> `development/tests.md` +- r-code (ch16) -> `development/r-code.md` +- other-than-Rcode (ch17) -> `development/compiled-thirdparty.md` +- shiny (ch18) -> `development/shiny.md` +- ai-policy-third-party (ch19) -> `development/ai-policy.md` +- non-software (ch20) -> `development/non-software-pkgs.md` +- gitignore (ch21) -> `development/gitignore.md` +- build-check-bioccheck (ch22), conclusion (ch23) -> `development/build-check-bioccheck.md` + +Maintenance (ch 24-30) -> `maintenance.md`: +- package-maintenance, git-version-control (ch24), versionnum (ch25), + troubleshooting-build-report (ch26), debugging-cc-code (ch27), deprecation (ch28), + package-end-of-life-policy (ch29), branch-rename-faqs (ch30) + +Reviewer (ch 31-33) -> `reviewer.md`: +- reviewer-resources-overview, review-expectation (ch31), reviewtools (ch32), + review-volunteer-chapter (ch33) + +Appendices (A-H) -> `appendices.md`: +- use-devel (A), advanced-build-options (B), querying-web-resources (C), c-fortran (D), + cmavericks-best-practices (E), man-links (F), booknews (G), references-1 (H) + +Not chapter-derived (maintain by hand): +- `index.md` (router), `workflow.md` (runbook), this file. diff --git a/knowledge/appendices.md b/knowledge/appendices.md new file mode 100644 index 0000000..edc4846 --- /dev/null +++ b/knowledge/appendices.md @@ -0,0 +1,64 @@ +# Appendices + +Covers: the key actionable point of each appendix (A-H) in the Bioconductor package development guide. + +## Appendix A - Using Devel Bioconductor + +Develop against Bioconductor devel so your package is ready when devel becomes the next release. The R version you pair with devel depends on the time of year (R releases once a year in mid-April; Bioconductor releases twice a year, mid-April and mid-October): + +- Mid-April to mid-October: use R-release (the current released R) with Bioconductor devel. +- Mid-October to mid-April: use R-devel (daily build) with Bioconductor devel, because a new R is coming in April. + +Rule of thumb: target the R version that users will have when the current devel branch becomes the release branch. + +Install/switch to devel: + +```r +if (!requireNamespace("BiocManager", quietly = TRUE)) + install.packages("BiocManager") +BiocManager::install(version = "devel") +BiocManager::valid() # check all packages are the correct devel versions +``` + +`BiocManager::install(version = "devel")` flips the active version to devel; `BiocManager::valid()` reports any out-of-date or "too new" packages. For the mid-October to mid-April window, first install R-devel (source from stat.ethz.ch/R/daily, macOS from mac.r-project.org, Windows rdevel from CRAN) and run the same commands in that R. + +## Appendix B - Advanced Build Options + +- Skip unsupported platforms via `Config/Bioconductor/UnsupportedPlatforms` in DESCRIPTION (or legacy `UnsupportedPlatforms:` in `.BBSoptions`); platforms are win, mac, etc. +- Long tests (>40 min): put them in a `longtests/` dir and set `RunLongTests: TRUE` in `.BBSoptions`; they run weekly (Saturdays, up to 6 hours) and their failures do not block propagation. Keep normal `tests/` under 40 minutes. +- GPU packages: declare `GPU_reliance: required` or `optional` in `.BBSoptions`. + +## Appendix C - Querying Web Resources + +- Keep downloads reasonably sized so `R CMD check` finishes well under 10 minutes. +- Never use unbounded `while()` retries; set an explicit max number of attempts (e.g. an `N.TRIES` loop wrapped in `tryCatch()`) and fail with a clear message that includes the URL and error. +- Respect `getOption("timeout")` and check HTTP status from `httr::GET()` / `download.file()`. + +## Appendix D - C and Fortran + +- Follow the "System and foreign language interfaces" section of Writing R Extensions. +- Use R's internal facilities (`R_alloc`, R's RNG) instead of system equivalents; register native routines. +- Add `R_CheckUserInterrupt()` in long C-level loops. +- Use `Makevars`/`Makefile` sparingly. During development enable all warnings and disable optimization, e.g. gcc `-Wall -Wextra -pedantic -O0 -ggdb`, clang `-Weverything -O0 -g` (put user Makevars in `~/.R/`). + +## Appendix E - C++/Mavericks Best Practices + +- Prefer Rcpp for C++ integration; use BH for Boost instead of bundling it. +- Define `R_NO_REMAP` and use fully-qualified names (`Rf_length()`, `std::map`); never `using namespace std;` (especially in headers). +- Keep R headers out of `extern "C"` blocks. +- Avoid dereferencing/incrementing past-the-end iterators (segfaults). Regenerate old SWIG code with a C++11-capable SWIG. + +## Appendix F - Man Page Links + +- `\linkS4class{}` cross-references (e.g. to `SummarizedExperiment`) can trigger check warnings. To resolve: put the target package in `Depends:` (not just `Imports:`), add `#' @import ` in roxygen, and run `devtools::document()` so NAMESPACE gets the `import()` entry. + +## Appendix G - Book News + +- Changelog for the guidelines. 1.0.0 (2021-06-02) initial release; 1.0.1 (2021-08-19) added the package-naming section. Tracks when new guidance was added. + +## Appendix H - References + +- Bibliography. Key citation: Soneson et al. (2025), "Eleven Quick Tips for Writing a Bioconductor Package," PLoS Computational Biology 21(3):e1012856. + +Source: https://contributions.bioconductor.org/use-devel.html , https://contributions.bioconductor.org/advanced-build-options.html , https://contributions.bioconductor.org/querying-web-resources.html , https://contributions.bioconductor.org/c-fortran.html , https://contributions.bioconductor.org/cmavericks-best-practices.html , https://contributions.bioconductor.org/man-links.html , https://contributions.bioconductor.org/booknews.html , https://contributions.bioconductor.org/references-1.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/ai-policy.md b/knowledge/development/ai-policy.md new file mode 100644 index 0000000..b135855 --- /dev/null +++ b/knowledge/development/ai-policy.md @@ -0,0 +1,29 @@ +Covers: Chapter 19 - AI and third-party code policy + +# AI-Generated and Third-Party Code Policy + +## Disclosure +- Non-trivial code from AI tools or external sources must be disclosed: + discuss it in the submission issue and state it in the PR description. +- Cite provenance directly in the contributed code (in-code comment). +- Use an attribution line, for example: + - `Assisted-by: [AI name]` + - `Code copied from: [source/URL]` + +## Licensing +- All newly contributed code must be redistributable under the package's + existing license. +- The developer is responsible for confirming license compatibility before + incorporating any external or AI-generated code. + +## Maintainer responsibility +- The maintainer retains full accountability for all incorporated code, + regardless of origin, including its reliability, ongoing maintenance, and + bug fixes. + +## Scope +- The policy targets "non-trivial portions." Minor snippets or standard + boilerplate may fall outside it, but when in doubt, disclose. + +Source: https://contributions.bioconductor.org/ai-policy-third-party.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/build-check-bioccheck.md b/knowledge/development/build-check-bioccheck.md new file mode 100644 index 0000000..d0bf3fd --- /dev/null +++ b/knowledge/development/build-check-bioccheck.md @@ -0,0 +1,57 @@ +Covers: Chapters 22 and 23 - Build, check, BiocCheck, and conclusion + +# Build, Check, and BiocCheck + +Run all three tools successfully before submission. Build and check against the +current Bioconductor devel version, using the matching R version (Bioconductor +releases are tied to specific R versions). + +## Exact commands +Build the source tarball first, then check the tarball it produces: + +``` +R CMD build MyPackage +R CMD check MyPackage_0.99.0.tar.gz +``` + +Then run BiocCheck. For a git clone / working directory: + +```r +BiocCheck::BiocCheckGitClone() +``` + +For a new-package submission (runs the stricter new-package rules): + +```r +BiocCheck::BiocCheck('new-package' = TRUE) +``` + +BiocCheck can also be run on the built tarball. During development, +`devtools::check()` is a convenient wrapper around `R CMD build`/`check`. + +## Pass/fail gate +- BiocCheck is the submission gate. The package MUST pass with no ERRORs and no + WARNINGs. Address NOTEs where possible; unresolved notes may be questioned in + review. +- `R CMD build` and `R CMD check` must complete without ERROR. +- New packages should carry version `0.99.x` in DESCRIPTION. + +## What BiocCheck enforces (selection) +- Bioconductor coding style, dependency and NAMESPACE correctness. +- Presence of a vignette, runnable examples, and unit tests. +- Correct DESCRIPTION fields including valid `biocViews`. +- No forbidden files tracked in git (see gitignore chapter). +- Function length, line length, and other style thresholds. + +## CI +- GitHub Actions workflows can mimic the submission environment; see the + R-Universe Bioconductor integration docs. + +## Submission (conclusion) +- All contributions undergo formal peer review. +- Submit through the official GitHub package submission repository/tracker. +- See "How to Build a Bioconductor Package with RStudio" for a walkthrough. +- Once build, check, and BiocCheck are clean, the package is ready to submit. + +Source: https://contributions.bioconductor.org/build-check-bioccheck.html and https://contributions.bioconductor.org/conclusion.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/compiled-thirdparty.md b/knowledge/development/compiled-thirdparty.md new file mode 100644 index 0000000..da24bb3 --- /dev/null +++ b/knowledge/development/compiled-thirdparty.md @@ -0,0 +1,32 @@ +Covers: Chapter 17 - Code other than R (compiled and third-party) + +# Compiled Code and Third-Party Code + +## General rules +- Compiled code must follow the "System and foreign language interfaces" + section of the Writing R Extensions (R-exts) manual. +- Use `Makevars`/`Makefile` sparingly; they are often unnecessary. See the + "Configure and cleanup" section of R-exts. +- Code must be portable and build across all supported platforms + (Linux, macOS, Windows). Test on all before submission. +- Place compiled sources in `src/`. + +## Language-specific guidance +- C++: use Rcpp for cross-platform C++ integration (see the Rcpp Gallery). +- Fortran: consider dotCall64 for modern Fortran integration. +- Python: use basilisk to configure Python environments automatically so users + need no manual install. reticulate is at developer discretion. +- CMake-based builds: use the biocmake package. + +## Third-party code responsibilities +- Do not bundle external libraries that duplicate functionality already + provided by supported R/Bioconductor packages. +- Maintainers take full responsibility for any bundled third-party code: + keep it updated with upstream bug fixes and releases. +- Complex external libraries may require you to provide pre-built binaries for + some platforms. +- Ensure the license of any bundled third-party code is compatible with, and + redistributable under, the package license. + +Source: https://contributions.bioconductor.org/other-than-Rcode.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/data.md b/knowledge/development/data.md new file mode 100644 index 0000000..2f7c460 --- /dev/null +++ b/knowledge/development/data.md @@ -0,0 +1,50 @@ +Covers: Chapter 14 - Including data in a package + +# Package Data + +## Size limits and where large data goes +- Keep the whole package small. Source tarballs should stay under roughly + 5 MB for software packages; do not bloat a software package with data. +- Large data sets must NOT be committed to the package. There is no Git-LFS + support. Distribute large data through ExperimentHub or AnnotationHub. +- Traditional (self-contained) experiment data packages need pre-approval on + the bioc-devel mailing list. Prefer Hub-based distribution instead. + +## Directory conventions +| Location | Purpose | Access | +|-----------------|----------------------------------|--------------------| +| `data/` | Exported R datasets | `data("mydata")` | +| `inst/extdata/` | Raw files parsed by workflows | `system.file()` | +| `R/sysdata.rda` | Internal, non-exported data | package-internal | + +- Small data used by examples, vignettes, and tests may ship directly in the + package (`data/` or `inst/extdata/`). +- Store scripts that generated the data in `inst/scripts/` (or `data-raw/`). + +## Formats and compression +- Preferred format for `data/`: `.RData` created with `save()`. Other formats + allowed (see `?data`). +- Compress all data files. +- Avoid `LazyData: true`. Despite general R advice, Bioconductor recommends + against it because it slows package loading when data is large. + +## Documentation requirements +- Every dataset must be documented: creation method, source, and intended use. +- Raw files in `inst/extdata/` need metadata describing derivation and format. + +## Caching and file-writing restrictions +- Forbidden: downloading or writing files to the user home directory, working + directory, or the installed package directory. +- For persistent caching use BiocFileCache (preferred) or + `tools::R_user_dir(package, which = "cache")`. +- For non-persistent scratch files use `tempdir()` / `tempfile()`. + +## ExperimentHub / AnnotationHub +- ExperimentHub: curated experiment data stored externally, retrieved on demand. +- AnnotationHub: annotation resources (genomic identifiers, mappings) served + externally. +- Both give a lightweight package: metadata + man pages + vignette, with the + heavy data hosted remotely. See the HubPub `CreateAHubPackage` vignette. + +Source: https://contributions.bioconductor.org/data.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/documentation.md b/knowledge/development/documentation.md new file mode 100644 index 0000000..6d6d6d7 --- /dev/null +++ b/knowledge/development/documentation.md @@ -0,0 +1,40 @@ +# Documentation + +Covers: Chapter 13 - Documentation (man pages and vignettes). + +## Man pages (Rd files) +- Every exported function and class must have a man page. +- Class documentation must be very detailed about the structure and type of + information stored in the object. +- Data man pages must include provenance information and data structure + information. +- A package-level man page is encouraged, accessible via `?`. +- Documentation may be authored with roxygen2 (comments compiled to Rd) or written + directly as Rd. + +## Examples (runnable) +- All man pages should have runnable examples. +- `\donttest` and `\dontrun` are generally not allowed except with proper + justification. +- If wrapping is unavoidable, prefer `\donttest` over `\dontrun`. + +## Vignettes +- At least one vignette is required, in Rmd (recommended), qmd (Quarto), or Rnw + (Sweave) format. +- Code must be executable and demonstrate actual functionality: "Non-trivial + executable code is a must!!! Static vignettes are not acceptable." +- Each vignette should include an Introduction, Installation instructions, a Table + of Contents (when appropriate), and a `sessionInfo()` call. +- Installation instruction chunks must use `eval = FALSE`. +- R Markdown vignettes should use the BiocStyle package for rendering. +- Quarto vignettes require the Quarto command-line tool to be listed in + DESCRIPTION. + +## Overall documentation standards +- Vignettes demonstrating core functionality. +- Man pages for all exported functions, each with runnable examples. +- Well-documented data structures and datasets. +- References to the methods used and to related packages. + +Source: https://contributions.bioconductor.org/docs.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/general-dev.md b/knowledge/development/general-dev.md new file mode 100644 index 0000000..608cfc3 --- /dev/null +++ b/knowledge/development/general-dev.md @@ -0,0 +1,59 @@ +# General Development Requirements + +Covers: Chapter 3 - General guidelines, and Chapter 4 - Important Bioconductor +package development features. + +## Development environment +- Develop against the devel version of Bioconductor and use devel Bioconductor + packages. +- Use a recent R-devel matching the current Bioconductor devel branch. + +## Pre-submission gate (build and check) +- Must pass `R CMD build` and `R CMD check` with NO errors and NO warnings on + recent R-devel. +- Must pass `BiocCheck::BiocCheckGitClone()` with no errors/warnings. +- Must pass `BiocCheck::BiocCheck('new-package' = TRUE)` with no errors/warnings. +- All ERRORs, WARNINGs, and NOTEs must be addressed or explicitly justified. + +## Hard numeric thresholds +- Source package produced by `R CMD build`: **< 10 MB**. +- Individual files (software packages): **<= 5 MB** each. +- `R CMD check --no-build-vignettes` runtime: **< 10 minutes**. +- Memory usage across vignettes, examples, and tests: **< 8 GB**. +- Use lossy compression (e.g., pngquant) to shrink large images/screenshots. + +## File hygiene +- Do not include filenames that differ only in case (cross-platform safety). +- Exclude unnecessary files: `.DS_Store`, `.project`, `.git`, cache files, logs, + `*.Rproj`, `*.so`. +- Use `.gitignore` to keep undesirable files out of the repository. +- Keep application-specific tooling (GitHub Actions, devtools config) on separate + branches, not in the submitted package. +- R CMD check options are customized by Bioconductor via flags configurable + through the `R_CHECK_ENVIRON` environment variable. + +## biocViews (required feature) +- The DESCRIPTION file MUST contain a `biocViews:` field (case-sensitive, + lowercase 'b'). +- Choose terms from only ONE category: Software, Annotation Data, Experiment Data, + or Workflow. +- Use leaf-level terms rather than broad parent categories. +- Terms must match the official hierarchy exactly (spelling and capitalization); + consult the devel branch biocViews list. +- Submission validation checks that biocViews are present, valid, and from a + single category. + +## Vignettes (required feature) +- Every submitted package must have at least one Rmd (preferred) or Rnw vignette. +- Render with `BiocStyle::html_document`. +- Vignettes must contain evaluated (non-trivial, runnable) R code. +- Include a detailed introduction motivating inclusion in Bioconductor and, where + relevant, compare against existing similar packages. + +## Reuse existing infrastructure +- Reuse established Bioconductor classes and methods where appropriate (see the + Common Bioconductor Methods and Classes guidance / methods-classes.md). + +Source: https://contributions.bioconductor.org/general.html and +https://contributions.bioconductor.org/important-bioconductor-package-development-features.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/gitignore.md b/knowledge/development/gitignore.md new file mode 100644 index 0000000..14f01c1 --- /dev/null +++ b/knowledge/development/gitignore.md @@ -0,0 +1,34 @@ +Covers: Chapter 21 - .gitignore + +# .gitignore + +- Keep a single `.gitignore` file at the top level of the package. Do not nest + multiple `.gitignore` files. +- Certain system/generated files must NOT be tracked. BiocCheck flags them if + committed. They may exist locally but must be excluded. + +## Files that must be excluded (BiocCheck-flagged) +Configuration and history: +- `.Renviron`, `.Rprofile`, `.Rhistory`, `.RApp.history` +- `.Rproj`, `.Rproj.user` +- `.seed`, `.exrc`, `.gdb.history` + +Build artifacts and compiled objects: +- `.o`, `.sl`, `.so`, `.dylib`, `.a`, `.dll`, `.def` +- `.log`, `.aux`, `.backups` + +System and IDE files: +- `.DS_Store` (macOS) +- `.project`, `.cproject`, `.settings`, `.tm_properties` +- `.directory`, `.dropbox` +- `unsrturl.bst` + +Git metadata that should not be present: +- `.gitattributes`, `.gitmodules`, `.hgtags` + +## Notes +- Do not commit build tarballs (`*.tar.gz`) or the `*.Rcheck/` directory. +- Do not commit large data files (see data chapter); they belong in a Hub. + +Source: https://contributions.bioconductor.org/gitignore.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/metadata-files.md b/knowledge/development/metadata-files.md new file mode 100644 index 0000000..f949c6b --- /dev/null +++ b/knowledge/development/metadata-files.md @@ -0,0 +1,121 @@ +# Metadata Files + +Covers: Chapters 6-12 - README, DESCRIPTION, NAMESPACE, NEWS, LICENSE, CITATION, +and system dependencies. + +## README (Chapter 6) +- README is optional but useful, especially for packages developed on GitHub. +- If present, it must clearly give Bioconductor installation instructions using + `BiocManager::install()`. +- Any installation code blocks must use `eval = FALSE` so they do not execute. +- Files with executable code (including `README.Rmd`) must NOT install packages, + download system dependencies, or download applications; assume dependencies are + already present. +- Declare external software in DESCRIPTION `SystemRequirements`, not in the README. +- `README.md` may be auto-generated from a vignette via `README.Rmd` (R Markdown + child documents) and `rmarkdown::render()`. + +## DESCRIPTION (Chapter 7) +Required fields: +- **Package** - must match the repository name (case-sensitive). +- **Title** - brief but descriptive summary. +- **Version** - `x.y.z` scheme. New submissions start at **0.99.0**. `y` is even + for release versions, odd for devel; `z` increments with each commit. +- **Description** - relatively short but detailed overview; at least three complete + sentences. +- **Authors@R** - required (use this, not `Authors:`). Must include the maintainer + with the `cre` role and an actively maintained email. Use a single maintainer. + Include ORCID in `comment` if available. Example: + `person("First", "Last", email = "me@x.org", role = c("cre", "aut"), comment = c(ORCID = "..."))`. +- **License** - standard R license spec, version-specific (see LICENSE section). +- **biocViews** - REQUIRED (case-sensitive, lowercase 'b'). At least two leaf + nodes, all from the same trunk/package type; single comma-separated line. + +Dependency fields (Depends, Imports, Suggests, Enhances): +- All dependencies must come from Bioconductor or CRAN. The `Remotes:` field is + NOT supported. +- List each package only once across these fields. +- **Imports** - functions/methods/classes used within the package namespace + (the usual place for dependencies). +- **Depends** - only for functionality essential to users; rarely more than 3 + packages (avoid Depends bloat). +- **Suggests** - packages used only in vignettes, examples, or conditional code. +- **Enhances** - optional performance packages such as `Rmpi` or `parallel`. +- Version specifications are usually not needed. + +Other fields: +- **LazyData** - omit `LazyData: TRUE` for large data packages (it slows loading). +- **SystemRequirements** - external software not auto-installed; add an INSTALL + file for non-trivial installs. +- **BugReports** - encouraged; link to the GitHub issues page. +- **URL** - source repo and help resources. +- **VignetteBuilder** - name the builder (e.g., `knitr`) when using vignettes. +- **BiocType** - required for Docker/Workflow submissions; values `Software`, + `ExperimentData`, `Annotation`. +- **Config/Bioconductor/UnsupportedPlatforms** - comma-separated list to exclude + platforms (`windows`, `windows-x64`, `macosx`, `macosx-x86_64`, `macosx-arm64`). + +## NAMESPACE (Chapter 8) +- Prefer `importFrom()` to import specific functions; use `import()` only when + importing many functions from one package. +- For Bioconductor classes, `import()` the whole package so full class + functionality is inherited automatically. +- Do NOT use broad export patterns: `exportPattern("^[[:alpha:]]+")` is strongly + discouraged and almost always not allowed. Export functions/generics + individually. +- Exported function names should use camelCase or underscores; avoid dots (dots + imply S3 dispatch). Functions beginning with `.` stay internal and are not + exported. +- Use `exportMethods()` / `exportClasses()` for S4 methods and classes, and + `useDynLib()` for compiled code. + +## NEWS (Chapter 9) +- Exactly one NEWS file per package, in one of: `./inst/NEWS.Rd`, `./inst/NEWS`, + `./inst/NEWS.md`, `./NEWS.md`, or `./NEWS`. +- NEWS files MUST use list elements/structure; plain text files are not allowed. +- Document the forthcoming release version, in non-technical language. +- Version heading format: + ``` + CHANGES IN VERSION X.Y.Z + ------------------------- + ``` +- Use section headers such as "NEW FEATURES" and "SIGNIFICANT USER-VISIBLE + CHANGES" with bullet points. +- Bioconductor compiles NEWS files into semi-annual release announcements. +- Validate formatting with `utils::news(package = "")`. + +## LICENSE (Chapter 10) +- Use R's standard license specifications (r-project.org/Licenses). Be version + specific (e.g., `GPL-2`). Core packages typically use `Artistic-2.0`. +- Forbidden: licenses restricting use (e.g., to academic or non-profit + researchers) and other restrictive licenses (CC BY-NC variants, ACM). +- For a non-standard license, add a full `LICENSE` file at the package root and + reference it as `file LICENSE`; the file must match the `License:` field. +- All dependencies must have compatible open-source licenses, and the package must + contain only code that can be redistributed under its license. + +## CITATION (Chapter 11) +- Place the file at `inst/CITATION`. It is optional but recommended. +- Validate with `readCitationFile("inst/CITATION")` (must run without errors) so it + displays correctly on the package landing page. +- Follow Writing R Extensions conventions; specify author/maintainer details for + correct name formatting. If absent, Bioconductor auto-generates a citation. +- Also include citations in help pages and vignettes. + +## System dependencies (Chapter 12) +- Declare external software in the DESCRIPTION `SystemRequirements` field, with an + optional INSTALL file giving install instructions for Linux, Windows, and Mac. +- System requirements must never be exclusive to a specific version; work with + current versions of the external software. +- Declaring a requirement does not guarantee Bioconductor will agree to install it. +- Discuss additional system requirements on bioc-devel@r-project.org before + development. Do not install system dependencies from within package code. + +Source: https://contributions.bioconductor.org/readme.html, +https://contributions.bioconductor.org/description.html, +https://contributions.bioconductor.org/namespace.html, +https://contributions.bioconductor.org/news.html, +https://contributions.bioconductor.org/license.html, +https://contributions.bioconductor.org/citation.html, +https://contributions.bioconductor.org/sysdep.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/methods-classes.md b/knowledge/development/methods-classes.md new file mode 100644 index 0000000..a9f488d --- /dev/null +++ b/knowledge/development/methods-classes.md @@ -0,0 +1,42 @@ +# Reusing Common Methods and Classes + +Covers: Chapter 5 - Common Bioconductor methods and classes. + +## Core principles +- Interoperability is required: packages are generally NOT accepted unless they + demonstrate interoperability, typically by reusing existing Bioconductor classes + and methods where appropriate. +- Bioconductor uses the S4 object system for genomic data because it provides + formal class definitions, multiple inheritance, and validity checking. +- New classes require strong justification and must clearly describe how they + interoperate with existing Bioconductor infrastructure. +- Before creating new classes, discuss the proposal on the bioc-devel mailing list + or Bioconductor Slack for community feedback. + +## Classes to reuse (by data type) +| Data type | Recommended class / package | +|-----------|-----------------------------| +| Count matrices, microarray data | `SummarizedExperiment::SummarizedExperiment()` | +| Genomic coordinates | `GenomicRanges::GRanges()` | +| Multi-sample genomic coordinates | `GenomicRanges::GRangesList()` | +| Variable-length / ragged coordinates | `RaggedExperiment::RaggedExperiment()` | +| DNA/RNA/protein sequences | `Biostrings::*StringSet()` | +| Gene sets / collections | `BiocSet::BiocSet()` or `GSEABase` equivalents | +| Multi-omics integration | `MultiAssayExperiment::MultiAssayExperiment()` | +| Single-cell data | `SingleCellExperiment::SingleCellExperiment()` | +| Mass spectrometry | `Spectra::Spectra()` | + +## Import / parsing methods to reuse +Use existing importers instead of writing custom parsers: +- Genomic file formats (BED, GFF, etc.): `rtracklayer` +- VCF: `VariantAnnotation` +- BAM / sequencing alignments: `Rsamtools`, `GenomicAlignments` +- FASTA sequences: `Biostrings` +- Mass spectrometry data: `Spectra` + +## When importing Bioconductor classes +- Import the full class package (via `import()`) so that full class functionality + is inherited automatically (see namespace guidance in metadata-files.md). + +Source: https://contributions.bioconductor.org/reusebioc.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/non-software-pkgs.md b/knowledge/development/non-software-pkgs.md new file mode 100644 index 0000000..d8683b0 --- /dev/null +++ b/knowledge/development/non-software-pkgs.md @@ -0,0 +1,41 @@ +Covers: Chapter 20 - Non-software packages + +# Non-Software Packages + +Non-software packages fall into two families: annotation packages and +experiment data packages. Hub-based distribution is preferred over +self-contained data packages. + +## Annotation packages +- Link identifiers (gene names, probe IDs) to related information + (chromosomal location, Gene Ontology categories, mappings). +- Must include proper documentation for the data provided. + +## Experiment data packages +- Contain curated datasets from an experiment, course, or publication, + typically a single dataset. +- Require documentation of the data (source, creation, use). +- Traditional self-contained experiment data packages are discouraged; prefer + the Hub approach. + +## Hub packages (AnnotationHub / ExperimentHub) +- Lightweight: data is stored externally (AWS S3, Azure Data Lakes, Ensembl, + other public sites) and fetched on demand. +- Must minimally contain: resource metadata, man pages describing the + resources, and a vignette. May include supporting R functions. +- Follow the `CreateAHubPackage` vignette in the HubPub package. + +## biocViews +- Annotation packages must include `AnnotationData` (and appropriate child + terms) in the DESCRIPTION `biocViews:` field. +- Experiment data packages must include `ExperimentData` (and child terms). +- Correct biocViews determine which of the three Bioconductor repositories the + package is assigned to. + +## Submission +- Submit through the GitHub package submission tracker. +- Indicate the package type (annotation, experiment data, workflow) at + submission. + +Source: https://contributions.bioconductor.org/non-software.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/package-name.md b/knowledge/development/package-name.md new file mode 100644 index 0000000..aa014b4 --- /dev/null +++ b/knowledge/development/package-name.md @@ -0,0 +1,37 @@ +# Package Name + +Covers: Chapter 2 - Choosing a package name. + +## Core rules +- The package name must match the GitHub repository name and is case-sensitive. +- The name must be unique: it must not already exist in Bioconductor (checked + case-insensitively) or on CRAN. +- The name should be descriptive of the package's purpose. +- Reusing archived or deprecated package names is strongly discouraged and often + will not be allowed. + +## How to check availability +- Try installing the proposed name; the install should FAIL if the name is free: + ```r + BiocManager::install("MyPackage") + ``` +- Alternatively, search the Bioconductor code base / package listings directly. +- Check for unintended meanings in other languages using a tool such as + wordsafety.com. + +## Names to avoid (forbidden / disallowed patterns) +- Names that create confusion with an existing package, function, or class name. +- Names implying temporal relationships (e.g., `ExistingPackage2`) or qualitative + upgrades (e.g., `ExistingPackagePlus`). +- Hate speech, slurs, or profanity. +- References to historical, ethical, or political contexts. +- Names invoking well-known people, characters, brands, places, or icons. +- Names with unintended meanings in foreign languages. + +## Renaming +- Bioconductor discourages renaming a package after acceptance. +- Renaming requires deprecating the old package and resubmitting for review, which + is time-consuming. Choose carefully up front. + +Source: https://contributions.bioconductor.org/package-name.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/r-code.md b/knowledge/development/r-code.md new file mode 100644 index 0000000..3e0ef27 --- /dev/null +++ b/knowledge/development/r-code.md @@ -0,0 +1,62 @@ +Covers: Chapter 16 - R code + +# R Code Style and Best Practices + +## Formatting (hard rules) +- Use `<-` for assignment; `=` only for named function arguments. +- Indent with 4 spaces. No tabs. +- No lines longer than 80 characters. +- Space around binary operators: `a == b`. No space around `=` in named + arguments: `somefunc(a=1, b=2)`. +- Full-line comments start with `##`, indented to surrounding code. + +## Naming +- Functions and variables: camelCase starting lowercase (`myFunction`). +- Classes: CamelCase starting uppercase (`MyClass`). +- Do NOT put `.` in function names (avoids S3 dispatch collisions). +- Prefix non-exported/internal functions with a dot: `.internalFunc`. + +## Vectorization and iteration +- Write `seq_len(n)` or `seq_along(x)`, NOT `1:n` or `1:length(x)` + (the latter break when length is 0). +- Prefer vectorized code over explicit `for` loops. +- Use `vapply()` instead of `sapply()` (type-safe). +- Pre-allocate and fill (via `lapply()`/`vapply()`); never copy-and-append in a + loop (that is O(n^2)). + +## Booleans +- Use `TRUE`/`FALSE`, never `T`/`F`. + +## Functions +- Write small functions; avoid functions longer than one screen. +- Give arguments defaults where sensible; validate with `stopifnot()` or checks. + +## Forbidden patterns +- `set.seed()` inside package/internal code. +- `browser()` left in code. +- Direct slot access with `@` or `slot()` - use accessor methods instead. +- `<<-` (superassignment). +- `system()` without justification - use `system2()`. +- Nested function definitions. +- Commented-out code blocks and TODO comments in published packages. + +## Messaging +- `message()` for diagnostic messages. +- `warning()` for unusual-but-handled situations. +- `stop()` for errors. +- `cat()`/`print()` only inside `show()` methods, not for general messaging. + +## Classes and methods +- Prefer S4 over S3. Provide constructor functions and `show()` methods. +- Use accessors, not direct slot access. +- Only define methods for classes exported by your own package. +- Reuse existing Bioconductor core classes rather than inventing new ones. + +## Web access, caching, parallelism +- Never write to the user home, working, or installed-package directory. +- Cache downloads via BiocFileCache or + `tools::R_user_dir(package, which="cache")`; use `tempfile()` for scratch. +- Parallel operations should default to 1 or 2 cores. + +Source: https://contributions.bioconductor.org/r-code.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/shiny.md b/knowledge/development/shiny.md new file mode 100644 index 0000000..244c29b --- /dev/null +++ b/knowledge/development/shiny.md @@ -0,0 +1,38 @@ +Covers: Chapter 18 - Shiny apps in packages + +# Shiny Apps + +## Code location and organization +- UI and server code for a submitted Shiny app must live under the package + `R/` directory (not in a top-level `app.R` / `inst/shiny`). +- Keep business logic out of `shinyApp()` calls. Build internal functions that + generate UI and server components separately, so logic is testable without + launching the app. +- Recommended file naming: + - `interface_*.R` - functions returning UI elements + - `outputs_*.R` - functions returning server outputs + - `observers_*.R` - functions creating reactive observers + - `utils_*.R` - misc processing helpers + +## Launching restrictions +- `shiny::runApp()` must NOT appear anywhere in the package source. +- Exported functions should RETURN a Shiny app object; the user calls + `runApp()` themselves. + +## Testing +- Unit-test all non-reactive functions (e.g. with testthat). +- Wrap untestable reactive code with `# nocov start` / `# nocov end`. +- Use shinytest2 to test visual and computational aspects of the app. +- Put reusable test fixtures in `tests/testthat/setup-*.R`. + +## Documentation +- Wrap example code that launches an app in `if (interactive()) { ... }`. +- Document internal functions with roxygen2 `@keywords internal`. +- Include screenshots in the vignette; optimize with pngquant or webshot2. + +## Review expectations +- Reviewers run `R CMD build`, `R CMD check`, and BiocCheck, test the app with + shinytest2, and check graceful error handling and responsive UI. + +Source: https://contributions.bioconductor.org/shiny.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/development/tests.md b/knowledge/development/tests.md new file mode 100644 index 0000000..cbfd777 --- /dev/null +++ b/knowledge/development/tests.md @@ -0,0 +1,42 @@ +Covers: Chapter 15 - Unit tests + +# Unit Testing + +## Framework choice +- Bioconductor slightly prefers testthat. RUnit and tinytest are also accepted. +- testthat: active development, rich assertions, integrates with devtools, + informative failures. +- tinytest: lightweight, zero dependencies. +- RUnit: long Bioconductor history but unmaintained since ~2010. +- Declare the framework in DESCRIPTION `Suggests:` (e.g. `Suggests: testthat`, + or `Suggests: RUnit, BiocGenerics`, or `Suggests: tinytest`). + +## Directory structure and naming +- testthat: tests in `tests/testthat/`, files start with `test`. + Set up with `usethis::use_testthat()`. +- RUnit: tests in `inst/unitTests/`, files match `test_*.R` + (e.g. `test_divideBy.R`). Add `tests/runTests.R` containing: + `BiocGenerics:::testPackage("MyPackage")`. +- tinytest: tests in `inst/tinytest/`. Add `tests/tinytest.R`: + `if (requireNamespace("tinytest", quietly=TRUE)) tinytest::test_package("PACKAGE")`. + +## What to test +- Test functions, methods, and classes with known inputs and expected outputs. +- Test edge cases and error conditions, not only the happy path. +- No hard minimum coverage percentage is mandated, but higher coverage is + expected and reduces bug risk. + +## Coverage measurement +- Use the covr package: `covr::package_coverage()`. + +## Running tests +- Full check (runs all tests): `R CMD check MyPackage`. +- During development: `devtools::test()` (reloads code and reruns). +- Manual: source the package and test files, then call the test function. + +## Long-running tests +- Consult the bioc-devel mailing list before adding tests that run very long, + so they do not slow the nightly builds. + +Source: https://contributions.bioconductor.org/tests.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/index.md b/knowledge/index.md new file mode 100644 index 0000000..51b303c --- /dev/null +++ b/knowledge/index.md @@ -0,0 +1,55 @@ +# Bioconductor knowledge base - router + +Task-oriented summaries of the official guide "Bioconductor Packages: Development, +Maintenance, and Peer Review" (https://contributions.bioconductor.org). Open the file that +matches the task. Each summary links back to its canonical chapter for the full text. + +## Start here +- New to submission, or want the whole path end to end: read `workflow.md` (the runbook). +- Just need one topic: use the map below. + +## Lifecycle router + +Authoring a package: +- Naming: `development/package-name.md` (ch 2) +- General setup + key features: `development/general-dev.md` (ch 3-4) +- Reusing Bioc classes/methods (SummarizedExperiment, S4, etc.): `development/methods-classes.md` (ch 5) +- Metadata files (README, DESCRIPTION, NAMESPACE, NEWS, LICENSE, CITATION, INSTALL): + `development/metadata-files.md` (ch 6-12) +- Documentation (vignettes, man pages, roxygen): `development/documentation.md` (ch 13) +- Package data + large data (ExperimentHub/AnnotationHub): `development/data.md` (ch 14) +- Unit tests: `development/tests.md` (ch 15) +- R code + Bioc code style: `development/r-code.md` (ch 16) +- Compiled / third-party code (C/C++/Fortran/Python): `development/compiled-thirdparty.md` (ch 17) +- Shiny apps: `development/shiny.md` (ch 18) +- AI policy + third-party code: `development/ai-policy.md` (ch 19) +- Non-software packages (ExperimentData/Annotation/Workflow): `development/non-software-pkgs.md` (ch 20) +- .gitignore: `development/gitignore.md` (ch 21) +- Build / Check / BiocCheck (the gate): `development/build-check-bioccheck.md` (ch 22-23) + +Submitting: +- Eligibility, package types, tracker issue, Single Package Builder: `01-submissions.md` (ch 1) +- The full sequence from local build to first release: `workflow.md` + +Maintaining (after acceptance): +- Git workflow (BiocCredentials, git.bioconductor.org dual remotes), version numbering, + build-report troubleshooting, deprecation, end of life, branch rename: `maintenance.md` (ch 24-30) + +Reviewing (and what reviewers check): +- Review expectations, reviewer tools, volunteering: `reviewer.md` (ch 31-33) + +Appendices: +- Using devel Bioconductor, advanced build options, querying web resources, C/Fortran, Mavericks, + Rd links, NEWS, references: `appendices.md` (A-H) + +## The gate (memorize) +See `workflow.md` and `development/build-check-bioccheck.md` for detail. A new package must: +- Pass `R CMD check` clean on current R-devel (no errors, no warnings). +- Pass `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; + < 8 GB memory to run vignettes/examples/tests. +- Set `Version: 0.99.0`; include `biocViews`, a vignette, and man pages; valid maintainer email; + not already on CRAN; hosted on the GitHub default branch. + +Source: https://contributions.bioconductor.org/index.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/maintenance.md b/knowledge/maintenance.md new file mode 100644 index 0000000..e6b2d72 --- /dev/null +++ b/knowledge/maintenance.md @@ -0,0 +1,187 @@ +# Covers: Chapters 24-30 - Bioconductor Package Maintenance + +Overview: this knowledge base covers ongoing maintenance and development of packages +already accepted into Bioconductor - git workflow, version numbering, build-report +troubleshooting, C/C++ debugging, deprecation, end-of-life policy, and branch renaming. + +## Chapter 24 - Git Version Control (post-acceptance workflow) + +Setup after acceptance: +- Register your SSH public key at the BiocCredentials app: `https://git.bioconductor.org/BiocCredentials/`. + Bioconductor initially seeds from your GitHub keys at `https://github.com/.keys`. +- Add the Bioconductor server as the `upstream` remote; keep `origin` = your GitHub repo: + ```bash + git remote add upstream git@git.bioconductor.org:packages/.git + git remote -v # origin -> GitHub, upstream -> git.bioconductor.org + ``` + +Sync before editing (pull from BOTH remotes so they stay in step): +```bash +git fetch --all +git merge upstream/devel +git merge origin/devel +``` + +Commit and push to BOTH remotes (every commit must bump the `z` version - see ch25): +```bash +git add +git commit -m "informative message" +git push upstream devel +git push origin devel +``` + +Push rules: +- Only two branches accept maintainer pushes: `devel` and the current release branch + (e.g. `RELEASE_3_6`). New branches CANNOT be created/pushed to the Bioconductor server. +- Use SSH exclusively for developer (read/write) access. + +Backport a bug fix from devel to release with cherry-pick: +```bash +git checkout RELEASE_3_6 +git cherry-pick +git push upstream RELEASE_3_6 +``` + +Builds: run once per day and take roughly 24 hours. A valid version bump is required +for any change to propagate; broken packages are not published to users. + +## Chapter 25 - Version Numbering + +Format is `x.y.z`: +- New (unaccepted) packages start at `0.99.0` in DESCRIPTION. +- `y` (middle): must be ODD in devel, EVEN in release. Maximum value is 99. +- `z` (patch): increment by 1 for EACH git commit in the devel branch. +- `x` (major): only ever changed by the Bioconductor team. + +Release transition mechanics: +- At release, a `0.99.z` package becomes `1.0.0` (first official release), and devel + continues from `1.1.0`. +- Generally, a package at `x.99.z` is bumped to `(x+1).0.0` in release and `(x+1).1.0` in devel. +- For a regular devel version like `1.1.25`, the team creates the release branch at `1.2.0` + and bumps devel to `1.3.0`. + +Critical: commits pushed WITHOUT a corresponding version bump do NOT propagate to the +repository seen by `BiocManager::install()`. + +## Chapter 26 - Troubleshooting the Build Report + +Propagation timeline: +- The Bioconductor Build System (BBS) pulls code daily around 2:30 PM EST; reports appear + around 11:30 AM EST next day. Commits after the cutoff slip to the following day + (36-48h lag possible). +- A valid version bump is ALWAYS required to propagate; broken packages are not published. + +Reproduce build failures locally: +1. Match the R version shown at the top of the relevant (devel/release) build report. +2. Update dependencies: `BiocManager::valid()` then `BiocManager::install()`. +3. Apply the build system's environment variables (Renviron.bioc). +4. Consider the official Bioconductor Docker images for a pre-configured environment. + +Common error categories: +- R 4.3+: vectors in `if`/`&&`/`||` conditions now error - reduce with `any()`/`all()`. +- R 4.0: missing S3 method registration in NAMESPACE; partial arg matching no longer + tolerated; `matrix` now extends `array` (use `is()`/`inherits()`, not `class(x) == ...`); + `data.frame()`/`read.table()` default `stringsAsFactors = FALSE`. +- Dependencies: CRAN binaries may lag a new R version; packages removed from CRAN/Bioc + force code restructuring; missing system libraries need a GitHub issue report. + +## Chapter 27 - Debugging C/C++ Code + +Setup: +- Compile without optimization and with debug symbols. Set `CFLAGS=-ggdb -O0` + (and `CXXFLAGS`) in `~/.R/Makevars`. +- Write a minimal script `buggy.R` that triggers the crash quickly and reliably. + +Tools: +- Valgrind (memory errors - invalid reads/writes, corruption behind segfaults): + ```bash + R -d valgrind -f buggy.R + ``` +- gdb / lldb (interactive): + ```bash + R -d gdb -f buggy.R + ``` + Key commands: `r` run, `b ` breakpoint, `bt` backtrace/call stack, + `p ` inspect a C variable, `call Rf_PrintValue()` to print an R object at C level. + +Workflow: locate the crash frame via `bt` (low-numbered frames are where execution +entered the bad code), inspect nearby state, test a hypothesized fix, rerun, confirm. +Debuggers reveal WHERE a crash happens; you deduce WHY. + +## Chapter 28 - Deprecation Guidelines + +Applies to functionality present in at least one official release; features added and +removed within the same devel cycle are exempt. Full lifecycle spans ~3 release cycles (~18 months). + +Function deprecation - three steps across cycles: +1. Deprecate (this devel cycle): inside the function call `.Deprecated("newFunc")` to emit + a warning; note the replacement in the man page. + ```r + myOldFunc <- function() { .Deprecated("myNewFunc") } + ``` +2. Defunct (next release cycle): replace `.Deprecated()` with `.Defunct()` so the function + errors instead of running; remove its man page and add it to a `MyPkg-defunct` man page. +3. Remove (following cycle): delete the code and its NAMESPACE export; keep only the defunct + man page so `help("MyPkg-defunct")` still works. + +Datasets: +- S3: add a deprecation class + custom `print` method that warns; next cycle make it error, then remove. +- S4: `setClass()` a deprecation subclass of the original + a `show` method that warns; same timeline. + +Whole packages: see the End-of-Life policy (ch29). + +## Chapter 29 - Package End-of-Life Policy + +The Core Team deprecates packages that: +- Fail to build/check cleanly on all platforms (maintainer gets a final ~2-week notice), or +- Have unresponsive maintainers (must answer support-site questions and package email, and + keep a valid maintainer email address). + +Timeline: +- Step I - Deprecation: ~6-month warning; users see deprecation notices and strikethrough on + build reports. +- Step II - Defunct: after one devel cycle without a fix, removed from nightly builds and + from `BiocManager::install()`. +- Final removal: package disappears in the following release cycle. + +Recovery: a package can return to active status if fixed within the deprecation period - +contact `maintainer@bioconductor.org`. A fully defunct/removed package must go through full +new-package review again. + +Maintainer-initiated: maintainers may voluntarily request deprecation (superseded, outdated, +unmaintainable) by notifying the bioc-devel mailing list. + +Orphaned packages: unresponsive-maintainer packages are labeled "orphaned"; community members +can request takeover by emailing the original maintainer and the Bioconductor team. + +## Chapter 30 - Branch Rename FAQs + +Bioconductor uses `devel` as the default branch name. Only the central repo at +`git.bioconductor.org` is renamed by the core team; maintainers must update their own local +and GitHub clones. Developers/maintainers are affected; end users are not. + +Rename a local branch to `devel`: +```bash +git branch -m master devel # or: git branch -m main devel +git fetch origin +git branch -u origin/devel devel +git remote set-head origin -a +``` + +On GitHub: change the default branch at +`https://github.com///branches` (edit pencil) to `devel`. + +Then push to Bioconductor: +```bash +git checkout devel +git push upstream devel +``` + +Clean up stale references: +```bash +git remote prune origin --dry-run # verify +git remote prune origin # execute +``` + +Source: https://contributions.bioconductor.org/git-version-control.html and https://contributions.bioconductor.org/versionnum.html and https://contributions.bioconductor.org/troubleshooting-build-report.html and https://contributions.bioconductor.org/debugging-cc-code.html and https://contributions.bioconductor.org/deprecation.html and https://contributions.bioconductor.org/package-end-of-life-policy.html and https://contributions.bioconductor.org/branch-rename-faqs.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/reviewer.md b/knowledge/reviewer.md new file mode 100644 index 0000000..768eefe --- /dev/null +++ b/knowledge/reviewer.md @@ -0,0 +1,46 @@ +# Reviewer Resources + +Covers: what reviewers check, reviewer tools, and how to volunteer to review. Doubles as an author-facing "what the reviewer will look for" checklist. + +## Chapter - Reviewer Resources Overview + +- Reviews are public: every submission review happens in the open on the `Bioconductor/BiocContributions` GitHub issue tracker. +- Anyone in the community may comment on any review, not just the assigned reviewer. +- All feedback must follow the Bioconductor Code of Conduct. +- Three resources make up this section: review expectations (what/how to review), reviewer tools (checklist + examples), and volunteer sign-up. + +## Chapter 31 - Review Expectations + +Who reviews and the commitment: + +- Reviewers must maintain at least one active Bioconductor package. +- Budget roughly 30 minutes to 1.5 hours per package review. +- Complete the review within 3 weeks of assignment. + +What reviewers evaluate (author-facing checklist of focus areas): + +- Ease of use of the package (intuitive API, sensible defaults). +- Documentation quality: complete man pages, a runnable vignette, clear examples. +- Well-written code: readable, maintainable, follows Bioconductor coding style. +- Interoperability: reuse of core Bioconductor classes and infrastructure rather than reinventing them. + +Note: Chapter 31 sets the process and focus areas. The detailed, item-by-item checklist lives in the reviewer tools chapter (below) and in the package development guidelines chapters. + +## Chapter 32 - Reviewer Tools + +- Package Review Checklist: a ready-made template reviewers paste into the relevant New Submission Tracker issue and tick off / update as the review proceeds. +- New Submission Tracker: the `Bioconductor/BiocContributions` GitHub issues are where reviews are conducted and the checklist is posted and tracked over time. +- Example reviews: completed reviews (e.g. MAGAR, HubPub, BiocSet) are linked as references for the expected standard and tone. +- The checklist plus automated `BiocCheck` / `R CMD check` output from the Single Package Builder drive the concrete pass/fail items an author must resolve. + +Authors: expect the reviewer to walk this checklist publicly in your submission issue, so pre-run `BiocCheck` and clear ERRORs/WARNINGs before requesting review. + +## Chapter 33 - Volunteer to Review + +- Anyone in the community can volunteer as a Bioconductor community reviewer; no special credentials required. +- Before signing up: read the Review Expectations chapter and the Bioconductor Code of Conduct. +- Sign up via the volunteer Google form (linked from the chapter). +- Volunteers are then assigned incoming packages from the New Submission Tracker. + +Source: https://contributions.bioconductor.org/reviewer-resources-overview.html , https://contributions.bioconductor.org/review-expectation.html , https://contributions.bioconductor.org/reviewtools.html , https://contributions.bioconductor.org/review-volunteer-chapter.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/workflow.md b/knowledge/workflow.md new file mode 100644 index 0000000..ea781eb --- /dev/null +++ b/knowledge/workflow.md @@ -0,0 +1,81 @@ +# End-to-end submission runbook + +The sequential path from local package to first Bioconductor release. The per-chapter files +under this directory are reference; this file is the process. Follow it top to bottom. + +## Phase 0 - Decide it belongs in Bioconductor +- Package addresses high-throughput genomic / biological data analysis. +- Reuses standard Bioconductor data structures (e.g. SummarizedExperiment, S4) where possible. + See `development/methods-classes.md`. +- Not already on CRAN; CRAN/Bioc-only dependencies. +- Pick a type: Software, Experiment Data, Annotation, or Workflow. See `01-submissions.md`. + +## Phase 1 - Build to the gate (before you submit) +Author against the development chapters, then clear every item below. Detail: +`development/build-check-bioccheck.md`, `development/general-dev.md`, `development/metadata-files.md`. + +Hard gate (all required): +- `Version: 0.99.0` in DESCRIPTION. See `maintenance.md` (version rule) and metadata-files. +- `biocViews` field present and valid; a vignette; man pages for exported objects. +- Valid maintainer email; maintainer == the person who will submit. +- `R CMD check` clean on current R-devel (no errors, no warnings). +- `BiocCheck::BiocCheckGitClone()` clean. +- `BiocCheck::BiocCheck('new-package'=TRUE)` clean (no errors, no warnings). +- Source build < 10 MB (`R CMD build`); `R CMD check --no-build-vignettes` < 10 min. +- Every individual file <= 5 MB; running vignettes/examples/tests uses < 8 GB memory. +- Bioc code style in R code: `<-`, 4-space indent, 80-col. See `development/r-code.md`. + +Use the current devel Bioconductor with the matching R version - see `appendices.md` (Appendix A) +and `development/general-dev.md`. Optional helper: `scripts/check-submission.R` runs the checks. + +## Phase 2 - Host on GitHub +- Push the package to the DEFAULT branch of a public GitHub repository (not a subdirectory, + not a non-default branch). +- Confirm `.gitignore` excludes build artifacts. See `development/gitignore.md`. + +## Phase 3 - Submit to the tracker +- Open a new issue at https://github.com/Bioconductor/Contributions/issues/new +- Issue TITLE = the package name. Body = link to your GitHub repo; confirm you have read the + guidelines and understand the review process. +- Annotation packages are the exception: email packages@bioconductor.org instead. +- Experiment Data that accompanies a software package: add to the same issue; submit the data + package first if the software depends on it. + +## Phase 4 - Single Package Builder (SPB) and review +- A webhook triggers the Single Package Builder; your package must build and check cleanly on + all platforms. Fix issues, push to GitHub, the build re-runs. +- A reviewer is assigned. Expect 2-6 weeks total. Respond within 2-3 weeks or the issue may be + closed for inactivity. +- On each change, bump the `z` in the version (0.99.0 -> 0.99.1 -> ...) and push. See + `maintenance.md` (version rule) and `reviewer.md` for what reviewers check. + +## Phase 5 - Acceptance and the Bioconductor git server +Once accepted (detail: `maintenance.md`, ch 24): +- Register your SSH public key at the BiocCredentials app + (https://git.bioconductor.org/BiocCredentials/). Bioconductor also reads keys from + https://github.com/.keys +- Add the Bioconductor remote and keep GitHub as origin: + - `git remote add upstream git@git.bioconductor.org:packages/.git` + - `git remote -v` should show origin (GitHub) and upstream (git.bioconductor.org) +- Sync then push to BOTH remotes: + - `git fetch --all` + - `git merge upstream/devel` (resolve conflicts if any) + - `git push upstream devel` (triggers the daily build, ~24h) and `git push origin devel` +- Only `devel` and the current `RELEASE_x_y` branches accept pushes. You cannot create new + branches on the Bioconductor server. Backport fixes with `git cherry-pick` onto the release + branch, then push that branch. + +## Phase 6 - Release and ongoing maintenance +- Bioconductor releases twice a year (around April and October). At the first release your + `0.99.z` becomes `1.0.0`. Devel and release version parity: `y` odd in devel, even in release. +- Monitor the daily/weekly build reports and fix breakages promptly. See `maintenance.md` + (troubleshooting build report, debugging C/C++). +- Subscribe to the bioc-devel mailing list; create a support.bioconductor.org account and watch + your package tag; keep the maintainer email in DESCRIPTION current. +- Deprecate/retire APIs per the lifecycle rules (`.Deprecated` -> `.Defunct` -> removed). See + `maintenance.md` (deprecation, end-of-life). + +Source: https://contributions.bioconductor.org/bioconductor-package-submissions.html, +https://contributions.bioconductor.org/git-version-control.html, +https://contributions.bioconductor.org/versionnum.html +Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/scripts/check-submission.R b/scripts/check-submission.R new file mode 100755 index 0000000..974de26 --- /dev/null +++ b/scripts/check-submission.R @@ -0,0 +1,91 @@ +#!/usr/bin/env Rscript + +# check-submission.R +# Run the Bioconductor pre-submission gate against a package and report pass/fail. +# Usage: +# Rscript check-submission.R [path/to/package] +# Defaults to the current directory. Requires R-devel plus the BiocCheck package. +# This is a convenience wrapper; the authoritative checks are R CMD check and BiocCheck. + +args <- commandArgs(trailingOnly = TRUE) +pkg_dir <- if (length(args) >= 1) args[[1]] else "." +pkg_dir <- normalizePath(pkg_dir, mustWork = TRUE) + +desc_path <- file.path(pkg_dir, "DESCRIPTION") +if (!file.exists(desc_path)) { + stop("No DESCRIPTION found in '", pkg_dir, "'. Point this at a package root.") +} + +pass <- character(0) +fail <- character(0) +note <- character(0) +record <- function(ok, msg) { + if (isTRUE(ok)) pass[[length(pass) + 1]] <<- msg + else fail[[length(fail) + 1]] <<- msg +} + +dcf <- read.dcf(desc_path) +field <- function(name) if (name %in% colnames(dcf)) trimws(dcf[1, name]) else NA_character_ + +# --- Static gate checks (no build required) --- +version <- field("Version") +record(!is.na(version) && grepl("^0\\.99\\.[0-9]+$", version), + sprintf("Version is 0.99.z for a new package (found: %s)", version)) + +record(!is.na(field("biocViews")) && nzchar(field("biocViews")), + "biocViews field present") + +maint <- field("Authors@R") +if (is.na(maint) || !nzchar(maint)) maint <- field("Maintainer") +record(!is.na(maint) && grepl("[[:alnum:]._%+-]+@[[:alnum:].-]+", maint), + "Maintainer with an email present") + +record(dir.exists(file.path(pkg_dir, "vignettes")), + "vignettes/ directory present") + +record(dir.exists(file.path(pkg_dir, "man")), + "man/ directory present") + +record(dir.exists(file.path(pkg_dir, "tests")), + "tests/ directory present") + +# Individual file size <= 5 MB +all_files <- list.files(pkg_dir, recursive = TRUE, full.names = TRUE, all.files = TRUE, no.. = TRUE) +big <- all_files[file.size(all_files) > 5 * 1024^2] +big <- big[!grepl("(^|/)\\.git/", big)] +record(length(big) == 0, + if (length(big) == 0) "No individual file exceeds 5 MB" + else sprintf("Files exceed 5 MB: %s", paste(basename(big), collapse = ", "))) + +# --- Dynamic checks (BiocCheck), if available --- +if (requireNamespace("BiocCheck", quietly = TRUE)) { + message("Running BiocCheck::BiocCheck('new-package' = TRUE) ...") + res <- tryCatch( + BiocCheck::BiocCheck(pkg_dir, `new-package` = TRUE), + error = function(e) { message("BiocCheck errored: ", conditionMessage(e)); NULL } + ) + if (!is.null(res)) { + n_err <- tryCatch(length(res$error), error = function(e) NA_integer_) + n_warn <- tryCatch(length(res$warning), error = function(e) NA_integer_) + record(isTRUE(n_err == 0), + sprintf("BiocCheck errors: %s", ifelse(is.na(n_err), "unknown", n_err))) + record(isTRUE(n_warn == 0), + sprintf("BiocCheck warnings: %s", ifelse(is.na(n_warn), "unknown", n_warn))) + } +} else { + note[[length(note) + 1]] <- paste( + "BiocCheck not installed - skipped R CMD check / BiocCheck. Install with", + "BiocManager::install('BiocCheck'), then run R CMD check and BiocCheck manually.") +} + +# --- Report --- +cat("\n=== Bioconductor pre-submission gate ===\n") +cat("Package:", field("Package"), "at", pkg_dir, "\n\n") +if (length(pass)) cat("PASS:\n", paste0(" - ", pass, collapse = "\n"), "\n", sep = "") +if (length(fail)) cat("\nFAIL:\n", paste0(" - ", fail, collapse = "\n"), "\n", sep = "") +if (length(note)) cat("\nNOTES:\n", paste0(" - ", note, collapse = "\n"), "\n", sep = "") + +cat("\nReminder: this wrapper does not replace `R CMD check` on R-devel. Run it, plus\n") +cat("BiocCheck::BiocCheckGitClone(), before opening a submission issue.\n") + +quit(status = if (length(fail) > 0) 1L else 0L) diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md new file mode 100644 index 0000000..a3f0fa5 --- /dev/null +++ b/skills/bioconductor-package-dev/SKILL.md @@ -0,0 +1,78 @@ +--- +name: bioconductor-package-dev +description: >- + Use when developing, maintaining, submitting, or reviewing a Bioconductor R package. Fires on + Bioconductor/Bioc package work, DESCRIPTION/NAMESPACE/NEWS/biocViews/BiocCheck, submission prep, + the Bioconductor Contributions tracker or git.bioconductor.org, and S4 or Bioconductor core + classes such as SummarizedExperiment - even when the user does not say "Bioconductor" explicitly + but the package clearly targets it. Provides the official contribution rules, the pre-submission + gate, version numbering, and a topic router into curated per-chapter summaries. +--- + +# Bioconductor package development + +Curated from the official guide "Bioconductor Packages: Development, Maintenance, and Peer +Review" (https://contributions.bioconductor.org). The detailed summaries live in +`${CLAUDE_PLUGIN_ROOT}/knowledge/` - open the file for the task instead of loading everything. + +## How to use this skill +1. Identify the lifecycle stage: Authoring, Submission, Maintenance, or Review. +2. Open the matching `knowledge/` file (map below) for the rules and exact values. +3. Apply the cross-cutting rules here (gate, version, style) to whatever you write or check. +4. For an end-to-end submission, follow `${CLAUDE_PLUGIN_ROOT}/knowledge/workflow.md`. + +## Router (open the file that matches the task) +- Whole submission path, start to finish: `knowledge/workflow.md` +- Full topic map: `knowledge/index.md` +- Submission mechanics + package types: `knowledge/01-submissions.md` +- Naming: `knowledge/development/package-name.md` +- General dev + key features: `knowledge/development/general-dev.md` +- Reusing Bioc classes/methods (S4, SummarizedExperiment): `knowledge/development/methods-classes.md` +- Metadata files (README/DESCRIPTION/NAMESPACE/NEWS/LICENSE/CITATION/INSTALL): + `knowledge/development/metadata-files.md` +- Documentation (vignettes, man pages): `knowledge/development/documentation.md` +- Package data + large data: `knowledge/development/data.md` +- Unit tests: `knowledge/development/tests.md` +- R code + code style: `knowledge/development/r-code.md` +- Compiled / third-party code: `knowledge/development/compiled-thirdparty.md` +- Shiny: `knowledge/development/shiny.md` +- AI policy + third-party code: `knowledge/development/ai-policy.md` +- Non-software packages: `knowledge/development/non-software-pkgs.md` +- .gitignore: `knowledge/development/gitignore.md` +- Build / Check / BiocCheck (the gate): `knowledge/development/build-check-bioccheck.md` +- Maintenance (git server, versioning, build reports, deprecation): `knowledge/maintenance.md` +- What reviewers check: `knowledge/reviewer.md` +- Appendices (devel Bioc, build options, C/Fortran, etc.): `knowledge/appendices.md` + +## Cross-cutting rules (apply always) + +Pre-submission gate - a new package is NOT submission-ready unless all hold: +- `R CMD check` clean on current R-devel (no errors, no warnings). +- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; + < 8 GB memory for vignettes/examples/tests. +- `Version: 0.99.0`; `biocViews` present; a vignette and man pages present; valid maintainer + email; not on CRAN; hosted on the GitHub default branch. + +Version rule: start `0.99.0`; in `x.y.z`, `y` is odd in devel / even in release (max 99); bump +`z` by 1 every commit; `0.99.z` becomes `1.0.0` at the first Bioconductor release; `x` changed +only by the Bioconductor team. + +Bioconductor code style (differs from tidyverse): `<-` assignment, 4-space indent, 80-column +lines; prefer vectorized code; avoid `1:n` (use `seq_len`/`seq_along`). + +## Submission and git server (short) +Host on the GitHub default branch, then open an issue (title = package name) at +https://github.com/Bioconductor/Contributions/issues/new (Annotation packages: email +packages@bioconductor.org). The Single Package Builder must pass on all platforms. After +acceptance: register an SSH key at BiocCredentials, add `upstream = git.bioconductor.org`, push +to both remotes; only `devel` and `RELEASE_x_y` branches accept pushes. Full sequence: +`knowledge/workflow.md`. + +## Optional helpers (in this plugin) +- `${CLAUDE_PLUGIN_ROOT}/scripts/check-submission.R` - runs `R CMD check` + `BiocCheck` and + reports pass/fail vs the gate (needs R + BiocCheck installed). +- `${CLAUDE_PLUGIN_ROOT}/templates/` - lean skeletons (DESCRIPTION, NEWS.md, .Rbuildignore, + .gitignore, inst/CITATION). For full scaffolding, prefer the `biocthis` package. + +For a full submission-readiness audit, use the `bioc-package-review` agent. diff --git a/templates/.Rbuildignore b/templates/.Rbuildignore new file mode 100644 index 0000000..8190437 --- /dev/null +++ b/templates/.Rbuildignore @@ -0,0 +1,13 @@ +^.*\.Rproj$ +^\.Rproj\.user$ +^\.github$ +^\.git$ +^\.gitignore$ +^README\.Rmd$ +^LICENSE\.md$ +^\.Rbuildignore$ +^doc$ +^Meta$ +^\.lintr$ +^codecov\.yml$ +^\.covrignore$ diff --git a/templates/.gitignore b/templates/.gitignore new file mode 100644 index 0000000..f001ed6 --- /dev/null +++ b/templates/.gitignore @@ -0,0 +1,22 @@ +# R +.Rproj.user +.Rhistory +.RData +.Ruserdata +*.Rproj + +# Build artifacts +*.tar.gz +*.Rcheck/ +src/*.o +src/*.so +src/*.dll + +# Vignette / docs output +doc/ +Meta/ +inst/doc/ + +# OS +.DS_Store +Thumbs.db diff --git a/templates/DESCRIPTION b/templates/DESCRIPTION new file mode 100644 index 0000000..2b01c33 --- /dev/null +++ b/templates/DESCRIPTION @@ -0,0 +1,29 @@ +Package: MyBiocPackage +Type: Package +Title: One Line Title In Title Case Without A Trailing Period +Version: 0.99.0 +Authors@R: + person("Given", "Family", email = "you@example.com", + role = c("aut", "cre"), + comment = c(ORCID = "0000-0000-0000-0000")) +Description: One paragraph describing what the package does. Use several complete + sentences. Do not start with the package name or "This package". Explain the + biological problem and the methods provided. +License: Artistic-2.0 +Encoding: UTF-8 +biocViews: Software, GeneExpression, RNASeq +Imports: + methods, + S4Vectors, + SummarizedExperiment +Suggests: + knitr, + rmarkdown, + BiocStyle, + testthat (>= 3.0.0) +VignetteBuilder: knitr +Depends: + R (>= 4.4.0) +RoxygenNote: 7.3.2 +URL: https://github.com/you/MyBiocPackage +BugReports: https://github.com/you/MyBiocPackage/issues diff --git a/templates/NEWS.md b/templates/NEWS.md new file mode 100644 index 0000000..9340e72 --- /dev/null +++ b/templates/NEWS.md @@ -0,0 +1,10 @@ +# MyBiocPackage 0.99.0 + +* Initial submission to Bioconductor. + + diff --git a/templates/inst/CITATION b/templates/inst/CITATION new file mode 100644 index 0000000..1b51d66 --- /dev/null +++ b/templates/inst/CITATION @@ -0,0 +1,14 @@ +citHeader("To cite MyBiocPackage in publications use:") + +bibentry( + bibtype = "Article", + title = "MyBiocPackage: a short descriptive title", + author = c(person("Given", "Family")), + journal = "Journal Name", + year = "2026", + volume = "0", + number = "0", + pages = "0-0", + doi = "10.0000/000000", + url = "https://github.com/you/MyBiocPackage" +) From b1084c66ffc84633f5dcbc8597aa5c1fafae71e8 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Thu, 23 Jul 2026 16:58:34 -0700 Subject: [PATCH 02/29] Untrack context/ working docs; add .gitignore context/ holds local working docs (session state, plan, TODO, refresh runbook) that should not ship with the shared repo. --- .gitignore | 6 + context/REFRESH.md | 65 ----------- context/SESSION_STATE.md | 53 --------- context/TODO.md | 51 -------- context/plan.md | 244 --------------------------------------- 5 files changed, 6 insertions(+), 413 deletions(-) create mode 100644 .gitignore delete mode 100644 context/REFRESH.md delete mode 100644 context/SESSION_STATE.md delete mode 100644 context/TODO.md delete mode 100644 context/plan.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4421733 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Working docs - local only, not shipped +context/ + +# OS +.DS_Store +Thumbs.db diff --git a/context/REFRESH.md b/context/REFRESH.md deleted file mode 100644 index 0d2492a..0000000 --- a/context/REFRESH.md +++ /dev/null @@ -1,65 +0,0 @@ -# Refresh runbook - -How to keep the `knowledge/` summaries in sync with the upstream Bioconductor guide. Run this -when Bioconductor cuts a release (around April and October) or whenever `pkgrevdocs` has notable -changes. Baseline state lives in `knowledge/SOURCES.md`. - -## 1. Detect drift - -Check the upstream commit against the pinned one in `knowledge/SOURCES.md`: - -- Current commit: `https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel` -> field `sha`. -- If it equals the pinned SHA, nothing changed. Stop. -- If it differs, list changed files: - `https://github.com/Bioconductor/pkgrevdocs/compare/...devel` - (Fetch these with the WebFetch tool; local `curl` has no CA bundle - use `curl -k` only for - reachability, not for content.) - -Also compare the guide TOC (`https://contributions.bioconductor.org/index.html`) against the -slug map in `knowledge/SOURCES.md` to catch added, removed, or renamed chapters. A renamed slug -silently breaks the link in the affected summary. - -## 2. Map changes to files - -For each changed chapter slug, look up its target file in the `knowledge/SOURCES.md` map. Only -those files need regenerating. A new chapter needs a new file plus a router entry in -`knowledge/index.md` and a map entry in `knowledge/SOURCES.md`. A removed chapter: delete its -section and fix references. - -## 3. Regenerate the changed summaries - -Re-run the same summary pass used to build these files (prompts and format spec are in -`context/plan.md`, "Content generation"). Use fable-model subagents. Format rules that must hold: -- Plain markdown, no emoji. -- Task-oriented (rules, thresholds, required/forbidden patterns), not a transcription. -- Each file ends with a `Source:` line (canonical chapter URL) and a `Fetched ` stamp. - -If the pre-submission gate numbers, the version rule, or the submission mechanics changed, also -update the inline copies in `AGENTS.md`, `skills/bioconductor-package-dev/SKILL.md`, -`agents/bioc-package-review.md`, and `knowledge/workflow.md` (these repeat the gate on purpose). - -## 4. Update the baseline - -In `knowledge/SOURCES.md` set the pinned commit SHA, commit date, and "Summaries fetched" date -to the new values. - -## 5. Verify - -- `PATH=/opt/homebrew/bin:$PATH claude plugin validate .` (expect: passed). -- Link check: extract `contributions.bioconductor.org/*.html` URLs from `knowledge/` and confirm - each returns 200 (`env -u CURL_CA_BUNDLE curl -k -sI ` locally, or WebFetch). -- Load test: `claude --plugin-dir . -p "..."` and confirm the skill + agent still load and the - changed content is reflected. - -## 6. Release - -- Bump `version` in `.claude-plugin/plugin.json` (plugin users only receive updates on a bump). -- Note the change (new NEWS/CHANGELOG line, or the commit message). -- Commit and push to `github.com/ybaeus/bioc_package_dev`. Marketplace users run - `/plugin marketplace update` then `/plugin update bioconductor-package-dev`. - -## Notes -- The canonical chapter link in each summary is always authoritative; a lagging summary still - points readers to the correct source. -- Environment: R 4.4.0 at /usr/local/bin; `claude` at /opt/homebrew/bin; `gh` is not installed - (use the GitHub REST API via WebFetch); local curl needs `-k`. diff --git a/context/SESSION_STATE.md b/context/SESSION_STATE.md deleted file mode 100644 index 39fbc85..0000000 --- a/context/SESSION_STATE.md +++ /dev/null @@ -1,53 +0,0 @@ -# Session State - bioc_package_dev - -Last updated: 2026-07-23 - -## Goal -Build reusable Claude tooling for Bioconductor package development in this repo: -a Skill (inline authoring/lifecycle guidance) plus a review Agent (submission-readiness -audit), backed by curated summaries of the official guide "Bioconductor Packages: -Development, Maintenance, and Peer Review" (https://contributions.bioconductor.org, -source repo: Bioconductor/pkgrevdocs). - -## Accomplished -- Environment prepped: - - Installed Node 26.5.0 via Homebrew (/opt/homebrew/bin - NOT on default PATH; prepend it - when running node/npx). - - Found and worked around a broken CURL_CA_BUNDLE env var (points to a missing enterprise - cert, causing curl error 77). Prefix network commands with `env -u CURL_CA_BUNDLE`. - Saved to memory. -- Installed caveman (JuliusBrussee/caveman) as a Claude Code plugin (user scope). Concise- - output skill. Activates next session via /caveman or "caveman mode"; stats via /caveman-stats. -- Wrote the build plan and handed it to Ultraplan for remote refinement. - - Local plan file: /Users/ybae/.claude/plans/i-would-like-to-polymorphic-quasar.md - - Ultraplan session: https://claude.ai/code/session_01GuiAoNksAgYvqYzd14b42h?from=cli - -## In progress -- Ultraplan refinement of the build plan - awaiting the refined plan to teleport back for - approval and implementation. - -## Next steps (once plan approved) -1. Scaffold repo layout: - - .claude/skills/bioconductor-package-dev/SKILL.md plus resources/ (per-chapter summaries - with links to live chapters; NOT vendoring pkgrevdocs) - - .claude/agents/bioc-package-review.md (submission-readiness audit; model: fable) - - Repo-root README.md so others can install and follow. -2. Generate chapter summaries with fable-model subagents (33 chapters plus appendices A-H; - full lifecycle: Submissions / Development / Maintenance / Reviewer). -3. Verify: structure and frontmatter parse, chapter URLs resolve, skill trigger smoke test, - agent smoke test against a dummy package skeleton. - -## Future idea (post bioc_package_dev) -- R to Python converter Skill or Agent: convert a Bioconductor (R) package to Python - (e.g. map Bioc classes like SummarizedExperiment / S4 to Python equivalents such as - AnnData / BiocPy, translate DESCRIPTION to pyproject, roxygen to docstrings, testthat to - pytest). Skill vs agent to be decided later; separate follow-on project, not part of the - current build. - -## Key facts / gotchas -- Node at /opt/homebrew/bin (prepend to PATH). Broken CURL_CA_BUNDLE - use - `env -u CURL_CA_BUNDLE`. -- Repo is otherwise greenfield (only LICENSE, .git, plus this file and memory). -- Decisions locked: both skill and agent; summaries plus links (no pkgrevdocs vendoring); - full lifecycle; content work done with the fable model. -- Doc convention: no emoji in any docs in this repo. diff --git a/context/TODO.md b/context/TODO.md deleted file mode 100644 index a62ef7c..0000000 --- a/context/TODO.md +++ /dev/null @@ -1,51 +0,0 @@ -# TODO - bioc_package_dev - -Running task list. See context/plan.md for the full approved plan. - -## Build -- [x] Scaffold repo structure + context/ (knowledge/, skills/, agents/, templates/, scripts/, .claude-plugin/) -- [x] Generate knowledge/ chapter summaries (all four parts + appendices) - - [x] index.md (router / chapter->file map) - - [x] workflow.md (end-to-end submission runbook) - - [x] 01-submissions.md (ch 1) - - [x] development/ (ch 2-23) - 14 files - - [x] maintenance.md (ch 24-30) - - [x] reviewer.md (ch 31-33) - - [x] appendices.md (A-H) -- [x] Cross-tool adapters: AGENTS.md, GEMINI.md, CLAUDE.md -- [x] Claude Code plugin: .claude-plugin/plugin.json, marketplace.json, skills/.../SKILL.md, agents/bioc-package-review.md -- [x] templates/ (DESCRIPTION, NEWS.md, .Rbuildignore, .gitignore, inst/CITATION) -- [x] scripts/check-submission.R -- [x] README.md - -## Verify -- [x] claude plugin validate - passed (after adding marketplace description) -- [x] claude --plugin-dir . load test - skill + agent both discovered -- [x] link check: all 44 knowledge/ chapter URLs resolve 200 (curl needs -k locally; no CA bundle) -- [x] skill trigger smoke test - "get my package ready for Bioconductor" surfaced skill, returned gate + version rule -- [x] script smoke test - check-submission.R ran against a dummy package (R 4.4.0), clean pass report, degrades without BiocCheck -- [ ] agent smoke test against a real package - deferred (needs a real package + BiocCheck) -- [x] cross-tool AGENTS.md self-contained (router + gate + version + links) - -## Maintenance -- [x] knowledge/SOURCES.md - slug->file map + pinned pkgrevdocs commit (9b078ea, 2026-07-20) -- [x] context/REFRESH.md - drift-detection + regen runbook -- Refresh trigger: each Bioc release (~April/October) or when pkgrevdocs default branch moves - -## Not yet done (optional / future) -- [ ] git add + commit + push to github.com/ybaeus/bioc_package_dev (not committed yet) -- [ ] Install BiocCheck to exercise the full dynamic gate -- [ ] Community-marketplace submission (claude plugin validate + review pipeline) - -## Confirmed facts -- GitHub repo: ybaeus/bioc_package_dev (install: /plugin marketplace add ybaeus/bioc_package_dev) -- Submission tracker: github.com/Bioconductor/Contributions (open issue, package name as title) -- This repo LICENSE: Apache-2.0 -- pkgrevdocs license: NOASSERTION (no standard license declared) - attribute by link, do not claim a license -- Node at /opt/homebrew/bin; broken CURL_CA_BUNDLE - use env -u CURL_CA_BUNDLE -- claude CLI at /opt/homebrew/bin/claude - -## Deferred -- Community-marketplace submission (claude plugin validate + review pipeline) -- skill-creator eval / description tuning -- Future: R->Python Bioconductor package converter diff --git a/context/plan.md b/context/plan.md deleted file mode 100644 index 14ad67c..0000000 --- a/context/plan.md +++ /dev/null @@ -1,244 +0,0 @@ -# Plan: Bioconductor Package Development — Cross-Tool Skill + Review Agent - -## Context - -`bioc_package_dev` is a greenfield repo (only `LICENSE`, `.git`, `SESSION_STATE.md`) meant to -hold reusable AI-assistant tooling for developing Bioconductor packages to the project's -official standards. Knowledge source: the official guide *"Bioconductor Packages: Development, -Maintenance, and Peer Review"* (https://contributions.bioconductor.org), source repo -`Bioconductor/pkgrevdocs`. - -Full TOC fetched + verified live this session: **33 numbered chapters + appendices A–H** across -four parts — Submissions (ch 1), Development Guidelines (ch 2–23), Maintenance (ch 24–30), -Reviewer Resources (ch 31–33), Appendix A–H. The real submission workflow (Contributions -tracker issue → Single Package Builder webhook → review → BiocCredentials SSH + -git.bioconductor.org dual remotes → release) and the hard numeric gates were verified from -chapters 1, general(3), versionnum(25), and git-version-control(24). - -Intended outcome: a shareable git repo whose Bioconductor knowledge is usable across multiple -AI coding assistants (Claude Code, plus Codex / Cursor / Gemini CLI / Copilot via the AGENTS.md -standard), packaged as a Claude Code plugin for one-command install, with a README so other -researchers can adopt it. - -**Decisions (confirmed this session):** -- Build **both** a Skill (inline authoring/lifecycle guidance) **and** an Agent (isolated - submission-readiness audit). -- pkgrevdocs handled via **curated per-chapter summaries + links to live chapters** — NO - vendoring, no submodule. -- **Cross-tool** is a goal: one portable knowledge base, thin per-tool adapters. -- Distribute as a **Claude Code plugin + marketplace** in this repo; document self-host install - now, note community-marketplace submission as a later step. -- Include a **lean** set of templates and **one** check script (rationale below). -- Full-lifecycle scope. Content generated with the **fable** model. No emoji in any docs. - -## Architecture: one knowledge base, thin adapters - -The chapter summaries + workflow runbook are **plain portable markdown** (`knowledge/`) — the -single source of truth, readable by any tool or human. Each assistant gets a thin adapter that -points into `knowledge/`, so there is no content duplication: - -- **AGENTS.md** (repo root) — the cross-tool entrypoint. Read natively by Codex, Cursor, Gemini - CLI, Copilot, Windsurf, and 20+ others. Carries the lifecycle router + pre-submission gate + - version rules inline (small), and references `knowledge/` for detail. -- **Claude Code plugin** — `skills/bioconductor-package-dev/SKILL.md` + `agents/…`, wrapping the - same `knowledge/` via `${CLAUDE_PLUGIN_ROOT}/knowledge`. Distributed via `marketplace.json`. -- **GEMINI.md** and **CLAUDE.md** — one-line files that import AGENTS.md, for tools that prefer - their own filename (both support imports/references). - -## Repo layout - -``` -bioc_package_dev/ -├── README.md # what it is + per-tool install/usage (NEW) -├── AGENTS.md # cross-tool entrypoint (Codex/Cursor/Gemini/Copilot) (NEW) -├── GEMINI.md # one-line import of AGENTS.md (NEW) -├── CLAUDE.md # one-line import of AGENTS.md (NEW) -├── LICENSE # existing -├── context/ # working docs, not shipped tooling (NEW) -│ ├── SESSION_STATE.md # moved from repo root -│ ├── plan.md # copy of this plan for repo-local reference -│ └── TODO.md # running task list -├── knowledge/ # SINGLE SOURCE OF TRUTH — portable markdown (NEW) -│ ├── index.md # router/TOC across all summaries (chapter→file map) -│ ├── workflow.md # END-TO-END submission runbook (stitches the process) -│ ├── 01-submissions.md # ch 1 (+ submission-overview) -│ ├── development/ -│ │ ├── package-name.md # ch 2 -│ │ ├── general-dev.md # ch 3–4 -│ │ ├── methods-classes.md # ch 5 -│ │ ├── metadata-files.md # ch 6–12 (README/DESCRIPTION/NAMESPACE/NEWS/ -│ │ │ # LICENSE/CITATION/INSTALL) -│ │ ├── documentation.md # ch 13 -│ │ ├── data.md # ch 14 -│ │ ├── tests.md # ch 15 -│ │ ├── r-code.md # ch 16 -│ │ ├── compiled-thirdparty.md # ch 17 -│ │ ├── shiny.md # ch 18 -│ │ ├── ai-policy.md # ch 19 -│ │ ├── non-software-pkgs.md # ch 20 -│ │ ├── gitignore.md # ch 21 -│ │ └── build-check-bioccheck.md # ch 22–23 -│ ├── maintenance.md # ch 24–30 -│ ├── reviewer.md # ch 31–33 -│ └── appendices.md # A–H -├── templates/ # lean scaffolding skeletons (NEW, optional) -│ ├── DESCRIPTION -│ ├── NEWS.md -│ ├── .Rbuildignore -│ ├── .gitignore -│ └── inst/CITATION -├── scripts/ # one check runner (NEW, optional) -│ └── check-submission.R # R CMD check + BiocCheck, pass/fail vs the gate -├── .claude-plugin/ -│ ├── plugin.json # plugin manifest (name, version, author) -│ └── marketplace.json # marketplace listing for /plugin install -├── skills/ -│ └── bioconductor-package-dev/ -│ └── SKILL.md # thin: router + gate; points at ${CLAUDE_PLUGIN_ROOT}/knowledge -└── agents/ - └── bioc-package-review.md # submission-readiness audit subagent (model: fable) -``` - -Note: plugin component dirs (`skills/`, `agents/`) live at **repo root**, NOT inside a -`.claude/` dir — the plugin root is this repo, and only `plugin.json` goes inside -`.claude-plugin/`. Grouping thin chapters per file (metadata ch 6–12, maintenance ch 24–30) -keeps `knowledge/` navigable while preserving progressive disclosure. - -`workflow.md` is the sequential publish runbook — added because per-chapter summaries are -reference, not process. It stitches the real developer path end to end: pre-submission gate → -host on GitHub default branch → open issue on the Contributions tracker → SPB (Single Package -Builder) webhook build must pass all platforms → reviewer iteration (bump z each commit, 2–6 -weeks) → acceptance → register SSH key at BiocCredentials, add `upstream` git.bioconductor.org -remote, dual-remote push → devel vs `RELEASE_x_y` branches (cherry-pick to backport) → ongoing -maintenance. Sources: ch1, ch24, ch25, appendix A. - -## Verified chapter → slug map (for summary generation) - -Fetch each from `https://contributions.bioconductor.org/`: - -- Submissions: submission-overview, bioconductor-package-submissions (ch1) -- Development: develop-overview, package-name(2), general(3), - important-bioconductor-package-development-features(4), reusebioc(5), readme(6), - description(7), namespace(8), news(9), license(10), citation(11), sysdep(12), docs(13), - data(14), tests(15), r-code(16), other-than-Rcode(17), shiny(18), - ai-policy-third-party(19), non-software(20), gitignore(21), build-check-bioccheck(22), - conclusion(23) -- Maintenance: package-maintenance, git-version-control(24), versionnum(25), - troubleshooting-build-report(26), debugging-cc-code(27), deprecation(28), - package-end-of-life-policy(29), branch-rename-faqs(30) -- Reviewer: reviewer-resources-overview, review-expectation(31), reviewtools(32), - review-volunteer-chapter(33) -- Appendix: use-devel(A), advanced-build-options(B), querying-web-resources(C), - c-fortran(D), cmavericks-best-practices(E), man-links(F), booknews(G), references-1(H) - -## Content generation - -Fetch + summarize with **fable**-model subagents in parallel, one batch per part. Each summary -is **task-oriented** (actionable rules, required/forbidden patterns, naming/version -conventions), NOT a transcription. Aim ~40–120 lines each. Every summary ends with a link to -its canonical chapter URL **and a "guide version / fetched" stamp** (staleness marker; refresh -bumps plugin `version`). Network note: prefix curl-based fetches with `env -u CURL_CA_BUNDLE` -(broken cert env var); Node at `/opt/homebrew/bin` if needed. Verify the live submission-tracker -repo name at build time — confirm `github.com/Bioconductor/Contributions` before hard-coding it. - -## Cross-cutting rules (shared by AGENTS.md and SKILL.md) - -Both entrypoints carry these inline (short); detail lives in `knowledge/`: -- **Pre-submission gate** (hard numbers): `R CMD check` clean on R-devel; - `BiocCheck::BiocCheckGitClone()` + `BiocCheck::BiocCheck('new-package'=TRUE)` clean; source - build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; < 8 GB - memory; `Version: 0.99.0`; hosted on GitHub default branch; not on CRAN; `biocViews` + - vignette + man pages present; valid maintainer email. -- **Version rule** (named): start `0.99.0`; `y` odd = devel / even = release (max 99); bump `z` - every commit; `0.99.z` becomes `1.0.0` at first Bioc release. -- **Bioc code style**: `<-` assignment, 4-space indent, 80-col; contrast with tidyverse. - -## SKILL.md design (Claude Code) - -- **Frontmatter `description`**: pushy triggering — fire on Bioconductor/Bioc package work, - `DESCRIPTION`/`NAMESPACE`/`NEWS`/BiocCheck/biocViews, submission prep, S4 / - `SummarizedExperiment`-style classes, even when "Bioconductor" isn't said explicitly. -- **Body**: short lifecycle router (Authoring → Maintenance → Submission/Review) mapping the - task to the right `knowledge/` file, plus the cross-cutting rules above. Detail stays in - `knowledge/`, referenced via `${CLAUDE_PLUGIN_ROOT}/knowledge`. - -## Review agent design (`agents/bioc-package-review.md`) - -- Frontmatter: `name`, `description` (triggers on "audit/review my package for Bioconductor - submission"), `model: fable`, read-mostly toolset (Read/Grep/Glob/Bash). -- Behavior: audit against the **explicit pre-submission gate** above, plus the broader checklist - from `knowledge/reviewer.md` + development chapters. Optionally invoke `scripts/check-submission.R` - when R is available. Return a structured report (blockers / warnings / suggestions) each mapped - to a specific guide chapter. Uses `knowledge/` as its base (no duplicated content). - -## Templates and scripts (lean, optional) - -- `templates/` — small curated skeletons (DESCRIPTION, NEWS.md, .Rbuildignore, .gitignore, - inst/CITATION). README points to **`biocthis`** for full scaffolding rather than reinventing - it. Rationale: high scaffolding value, plain files any tool/human copies, low churn. -- `scripts/check-submission.R` — one runner: `R CMD check` + `BiocCheck::BiocCheck()` then - report pass/fail against the gate. Requires R + BiocCheck installed (documented). Skip a - version-bump helper (trivial; `usethis`/manual). Rationale: operationalizes the gate we - emphasize without a maintenance-heavy toolchain. - -## Distribution (`.claude-plugin/` + README) - -- `plugin.json`: `name` = `bioconductor-package-dev`, `description`, explicit `version` (so - users only pull updates on a bump), `author`, `repository`, `license`. -- `marketplace.json`: lists this plugin so users run - `/plugin marketplace add /bioc_package_dev` then `/plugin install`. -- Self-host now; note `claude plugin validate` + community-marketplace submission as a later - step. Need the GitHub `/` to hard-code the install command (confirm at build). - -## README.md - -Repo-root README: one-line statement of what this repo is for; the repo layout tree; -prerequisites (R, Bioc-devel, `BiocManager`, `BiocCheck`, optional `biocthis`); **per-tool -install/usage** — -- Claude Code: `/plugin marketplace add /bioc_package_dev` → `/plugin install`; or clone - and `--plugin-dir`. -- Codex / Cursor / Gemini CLI / Copilot: clone and rely on `AGENTS.md` (Gemini also `GEMINI.md`). -- Manual fallback: copy `knowledge/` and point any tool at it. -—plus a refresh/versioning note, and attribution + license for pkgrevdocs (verify its license -at build; likely CC-BY). Plain text, no emoji. - -## Verification - -1. **Structure**: `.claude-plugin/plugin.json`, `skills/bioconductor-package-dev/SKILL.md`, - `agents/bioc-package-review.md`, `AGENTS.md`, and `knowledge/*` exist and parse (valid YAML - frontmatter, valid JSON manifests, no broken internal links). Run `claude plugin validate`. -2. **Link check**: every canonical chapter URL in `knowledge/` resolves (HTTP 200). -3. **Claude Code load test**: `claude --plugin-dir .` loads the plugin; skill appears as - `/bioconductor-package-dev:…` and the agent under Custom Agents. -4. **Skill trigger smoke test**: "help me get my R package ready for Bioconductor submission" - surfaces the skill; "write my DESCRIPTION for a Bioc package" routes to - `knowledge/development/metadata-files.md`. -5. **Cross-tool check**: confirm `AGENTS.md` is valid and self-contained enough that a - non-Claude tool pointed at the repo gets the router + gate + links. -6. **Agent smoke test**: invoke `bioc-package-review` against a minimal dummy package skeleton; - confirm a structured report referencing guide chapters. -7. Optional later: skill-creator eval loop to tune the SKILL `description` for reliable - triggering. - -## Deferred - -- Community-marketplace submission (`claude plugin validate` + review pipeline). -- Skill-creator formal eval + description-optimization loop. -- Future idea (separate project): R→Python Bioconductor package converter. - -## References (verified this session) - -Bioconductor guide (canonical knowledge source): -- Guide home + TOC: https://contributions.bioconductor.org/index.html -- Submissions ch1: https://contributions.bioconductor.org/bioconductor-package-submissions.html -- General requirements / gate: https://contributions.bioconductor.org/general.html -- Version numbering: https://contributions.bioconductor.org/versionnum.html -- Git workflow (dual remotes, BiocCredentials): https://contributions.bioconductor.org/git-version-control.html -- Source repo: https://github.com/Bioconductor/pkgrevdocs - -Cross-tool distribution basis: -- Claude Code plugins: https://code.claude.com/docs/en/plugins -- Claude Code skills: https://code.claude.com/docs/en/skills -- AGENTS.md standard (2026 guide): https://codersera.com/blog/agents-md-complete-guide-2026/ -- CLAUDE.md vs AGENTS.md vs GEMINI.md: https://inventivehq.com/blog/claude-md-vs-agents-md-vs-gemini-md -- Gemini CLI GEMINI.md docs: https://geminicli.com/docs/cli/gemini-md/ From 965752980f861e6dcba742880816f868821eadc1 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Thu, 23 Jul 2026 17:00:04 -0700 Subject: [PATCH 03/29] Fix README layout tree: drop untracked context/, add knowledge/SOURCES.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6a76de0..09df792 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ bioc_package_dev/ ├── knowledge/ # single source of truth - portable markdown summaries │ ├── index.md # topic router across all summaries │ ├── workflow.md # end-to-end submission runbook +│ ├── SOURCES.md # slug->file map + pinned upstream commit (for refresh) │ ├── 01-submissions.md # ch 1 │ ├── development/ # ch 2-23 (naming, metadata, docs, data, tests, code, ...) │ ├── maintenance.md # ch 24-30 @@ -34,8 +35,7 @@ bioc_package_dev/ │ └── check-submission.R # runs the pre-submission gate checks ├── .claude-plugin/ # Claude Code plugin + marketplace manifests ├── skills/bioconductor-package-dev/SKILL.md -├── agents/bioc-package-review.md -└── context/ # working docs (session state, plan, TODO) +└── agents/bioc-package-review.md ``` ## Prerequisites From c3e201e7bbf25bb30ee5d0a45a724209d770dd48 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:08:49 -0700 Subject: [PATCH 04/29] Add docs/REFRESH.md and README example prompts REFRESH.md moves out of gitignored context/ and covers all five tracked upstreams, not just pkgrevdocs. Co-Authored-By: Claude Opus 5 --- README.md | 31 ++++++++- docs/REFRESH.md | 169 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 docs/REFRESH.md diff --git a/README.md b/README.md index 09df792..08f9cbf 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # bioc_package_dev Reusable AI-assistant tooling for developing, maintaining, submitting, and reviewing -Bioconductor R packages to the project's official standards. The knowledge is distilled from the -official guide "Bioconductor Packages: Development, Maintenance, and Peer Review" -(https://contributions.bioconductor.org) into portable, task-oriented summaries that any AI +Bioconductor R packages to the project's official standards. The knowledge is distilled from the official guide "Bioconductor Packages: Development, Maintenance, and Peer Review" (https://contributions.bioconductor.org) into portable, task-oriented summaries that any AI coding assistant - or a human - can follow. It ships in two forms from one source of truth: @@ -77,6 +75,33 @@ These tools read `AGENTS.md` natively. Either work inside a clone of this repo, Read `knowledge/index.md` to find the topic, or `knowledge/workflow.md` for the full submission path. Point any assistant at the `knowledge/` directory. +## Example prompts + +Written for the main case: you already have a working R package or tool on GitHub and want to +contribute it to Bioconductor. Ask in your own words - these are shapes, not incantations. + +Getting oriented: + +- "I have an R package on GitHub, what do I need to do to submit it to Bioconductor?" +- "My package is on CRAN, can I move it to Bioconductor?" +- "What version number do I use for a new submission?" + +Working through specifics: + +- "I have 300 MB of reference data, where does it go?" (answer: not in the package) +- "Write me a function that iterates over samples" (applies Bioconductor style, not tidyverse) +- "My DESCRIPTION has no biocViews - what do I put there?" +- "Walk me through what happens after I open the Contributions issue." + +Checking readiness - these route to the `bioc-package-review` agent, which audits and reports +blockers rather than advising as you work: + +- "Audit my package for Bioconductor submission readiness." +- "Would this package pass review? Tell me what a reviewer would flag." + +The split in one line each: the skill guides you while you work; the agent renders a verdict on +demand. + ## Keeping it current The summaries are stamped with the date they were generated from the live guide, and diff --git a/docs/REFRESH.md b/docs/REFRESH.md new file mode 100644 index 0000000..d1a9918 --- /dev/null +++ b/docs/REFRESH.md @@ -0,0 +1,169 @@ +# Refresh runbook + +How to keep this repo in sync with the upstream projects it depends on. Run it when Bioconductor +cuts a release (around April and October), when the weekly `fidelity` CI job opens a drift issue, +or before any plugin version bump. + +Pinned baseline state lives in `knowledge/SOURCES.md`. This file is the procedure; that file is the +data. Both ship with the plugin. + +## Tracked upstreams + +Five, not one. A change to any of them can silently invalidate the guidance. + +| Upstream | How to detect a change | What it invalidates | +|---|---|---| +| `Bioconductor/pkgrevdocs` | commits API `sha` vs the pin; guide TOC vs the slug map | all of `knowledge/` | +| `Bioconductor/Contributions` `issue_template.md` | commits API for that path vs the pin | the pre-submission gate in `AGENTS.md`, `SKILL.md`, `agents/bioc-package-review.md` | +| `Bioconductor/BiocCheck` | release version + NEWS | what "BiocCheck clean" means; what the review agent should pre-empt | +| `lcolladotor/biocthis` | release version; the `use_bioc_*()` function list | the scaffolding commands the agent recommends, and `scripts/golden-path.R` | +| `grimbough/bioc-actions` | latest tag; each action's `action.yml` inputs | `.github/workflows/verify.yml` | + +Most of this is automated. `python3 scripts/verify.py --network` performs every detection step +below and, in CI, opens or updates a single tracking issue. Run it first; this runbook is what you +do with the result. + +## Priority order + +When more than one signal fires, work top-down. The ordering is by blast radius, not by effort. + +1. **Numeric or modal audit failure** - a specific claim in `knowledge/` is now factually wrong and + is being served to users. Fix immediately and restamp the file. +2. **A chapter URL 404s or redirects** - a chapter was renamed or removed, so the slug map is wrong. + Fix `knowledge/SOURCES.md` before touching content; every later step depends on the map. +3. **A mapped `.Rmd` changed** - those specific summaries are stale. Scoped refresh only. +4. **Pin moved but no mapped chapter changed** - cosmetic upstream change. Bump the pin, no content + work. + +## 1. pkgrevdocs (the guide) + +### Detect + +- Current commit: `https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel`, field `sha`. + Compare against the pinned SHA in `knowledge/SOURCES.md`. Equal means stop. +- Changed files: `https://github.com/Bioconductor/pkgrevdocs/compare/...devel` +- Chapter set: compare the guide TOC (`https://contributions.bioconductor.org/index.html`) against + the slug map in `knowledge/SOURCES.md`. This is the only way to notice an **added** chapter - + every existing file still checks out, so no per-file check will see it. + +### Map changes to files + +For each changed chapter slug, look up its target in the `knowledge/SOURCES.md` map. Only those +files need regenerating. + +- New chapter: new summary file, plus a router entry in `AGENTS.md`, plus a map entry in + `knowledge/SOURCES.md`. +- Removed chapter: delete its section and fix inbound references. +- Renamed chapter: update the slug map and the `Source:` footer of the affected file. + +### Regenerate + +Re-run the summary pass for the affected files only. Format rules that must hold, since +`scripts/verify.py` enforces them: + +- Plain markdown. No emoji anywhere. +- Task-oriented - actionable rules, thresholds, required and forbidden patterns. Not a + transcription of the chapter. +- Roughly 40-120 lines per file. +- Ends with a `Source:` line carrying the canonical chapter URL, and a `Fetched ` stamp. +- **Preserve upstream modality.** If the guide says "should" or "ideally", the summary says + "should" or "ideally". Hardening a recommendation into a requirement is a defect, and the + modal-verb audit in `verify.py --network` will fail on it. +- Restamp `Fetched` on every file you touch. `verify.py` fails when a file's last git commit is + newer than its stamp, so an edited-but-unstamped file is caught. + +If the gate numbers, the version rule, or the submission mechanics changed, also update the inline +copies in `AGENTS.md`, `skills/bioconductor-package-dev/SKILL.md` and +`agents/bioc-package-review.md`. These repeat the gate deliberately, and `verify.py` requires the +copies to stay identical. + +## 2. Contributions issue_template.md (the authoritative gate) + +The tracker's issue template is the checklist a submitter actually ticks. It, not our prose, is the +authority on what submission requires. + +- Detect: `https://api.github.com/repos/Bioconductor/Contributions/commits?path=issue_template.md&per_page=1` +- Content: `https://raw.githubusercontent.com/Bioconductor/Contributions/devel/issue_template.md` + +If a checkbox is added, removed, or reworded, update the gate in all three router files and in +`knowledge/01-submissions.md`. This template changes rarely - the pinned commit dates to 2021 - so +any movement is worth reading in full rather than skimming a diff. + +## 3. BiocCheck (the validator) + +This repo delegates all validation to BiocCheck rather than reimplementing it, so BiocCheck's +behavior is part of our contract with users. + +- Detect: release version at `https://bioconductor.org/packages/release/bioc/html/BiocCheck.html`; + changes at `https://raw.githubusercontent.com/Bioconductor/BiocCheck/devel/NEWS`. + +New or removed checks change what "BiocCheck clean" means. If a check is added that maps to advice +we give, `agents/bioc-package-review.md` should pre-empt it so users hear it from the agent before +they hear it from the tool. If a check is removed, drop any advice that existed only to satisfy it. + +The two gate items BiocCheck cannot measure are the timing ones - `R CMD check --no-build-vignettes` +under 10 minutes, and under 8 GB memory. Those need a real build; CI measures them. + +## 4. biocthis (the scaffolder) + +This repo recommends biocthis for scaffolding rather than shipping competing templates. The +commands we tell users to run must exist and must still produce an acceptable package. + +- Detect: release version at `https://bioconductor.org/packages/release/bioc/html/biocthis.html`; + function list at `https://api.github.com/repos/lcolladotor/biocthis/contents/R`. + +If a `use_bioc_*()` function is renamed or removed, the command block in `AGENTS.md`, `SKILL.md` +and `agents/bioc-package-review.md` is wrong, and `scripts/golden-path.R` will fail in CI. Those +four places carry the same block and `verify.py` requires them identical, so fix all four together. + +Note `biocthis_example_pkg()` is not a Bioconductor-ready generator - it wraps +`usethis::create_package()` in `tempdir()` and produces a bare skeleton. The Bioc-ready path is the +`use_bioc_*()` chain, which is what `golden-path.R` runs. + +## 5. bioc-actions (the CI harness) + +- Detect: `https://api.github.com/repos/grimbough/bioc-actions/tags`; input schemas at + `https://raw.githubusercontent.com/grimbough/bioc-actions///action.yml` for + `setup-bioc`, `build-install-check`, `run-BiocCheck`, `use-bioc-caches`. + +Pin a tag in `.github/workflows/verify.yml`, never a branch. A changed input name is a red build +with a confusing message; reading the `action.yml` diff first saves the debugging. + +## 6. Update the baseline + +In `knowledge/SOURCES.md`, set every pin you verified: the pkgrevdocs SHA and commit date, the +Contributions issue_template SHA, the BiocCheck and biocthis versions, the bioc-actions tag, and +the "Summaries fetched" date. + +## 7. Verify + +Run in this order; each is cheaper than the next and catches different failures. + +1. `python3 scripts/verify.py` - static checks. Must be clean. +2. `python3 scripts/verify.py --network` - drift, URL liveness, numeric and modal audits, gate vs + tracker, chapter coverage. Must be clean. +3. `PATH=/opt/homebrew/bin:$PATH claude plugin validate .` - manifests. Expect "passed". +4. `Rscript scripts/golden-path.R` then `R CMD build` and `BiocCheck` on the result - confirms the + scaffolding commands we recommend still produce an acceptable package. CI does this too, but + running it locally first means the first CI run is not the first execution. +5. `claude --plugin-dir . -p "..."` - load test; confirm skill and agent load and the changed + content is reflected. +6. `claude plugin eval . --ablation with-without` - behavioral cases. Costs tokens; run when the + `description` fields or the routing changed, not on every refresh. + +## 8. Release + +- Bump `version` in `.claude-plugin/plugin.json`. Plugin users only receive updates on a bump. +- Note the change in the commit message. +- Commit and push to `github.com/ybaeus/bioc_package_dev`. Marketplace users then run + `/plugin marketplace update` followed by `/plugin update bioconductor-package-dev`. + +## Notes + +- The canonical chapter link in each summary is always authoritative. A lagging summary still points + readers at the correct source, which is why the `Source:` footer is mandatory. +- Never edit a `knowledge/` file without updating its `Fetched` stamp in the same commit. +- Environment: `env -u CURL_CA_BUNDLE curl -sS ` fetches content fine; the + `CURL_CA_BUNDLE` variable is set to a stale path and unsetting it is sufficient. `-k` is not + needed and should not be used. R 4.4.0 at `/usr/local/bin`; `claude` at `/opt/homebrew/bin`; + `gh` is not installed locally, so use the GitHub REST API via curl. From d193a0d5c6fefe90439ac9e97c5fc611acb91d7b Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:17:31 -0700 Subject: [PATCH 05/29] Defer to BiocCheck and biocthis; fix over-hardened rules Delete scripts/check-submission.R and templates/ - both reimplement Bioconductor tooling that already does more, the reuse violation ch5 warns about. All three router files now carry an identical block pointing at BiocCheck and biocthis instead. Content fixes: - 01-submissions.md: eligibility is upstream "should", not "must"; CI files on a non-default branch is a recommendation, not a rule - workflow.md: add a "Converting an existing package" entry point - SOURCES.md: pin all five tracked upstreams; add the .Rmd column, so a GitHub compare can be joined to the affected summaries - SKILL.md: description retuned for conversion; router now lives only in AGENTS.md - README: layout tree, How this is verified, Built on Co-Authored-By: Claude Opus 5 --- AGENTS.md | 27 +++- README.md | 75 +++++++++-- agents/bioc-package-review.md | 44 +++++-- knowledge/01-submissions.md | 48 ++++--- knowledge/SOURCES.md | 153 +++++++++++++++-------- knowledge/workflow.md | 41 +++++- scripts/check-submission.R | 91 -------------- skills/bioconductor-package-dev/SKILL.md | 98 ++++++++------- templates/.Rbuildignore | 13 -- templates/.gitignore | 22 ---- templates/DESCRIPTION | 29 ----- templates/NEWS.md | 10 -- templates/inst/CITATION | 14 --- 13 files changed, 342 insertions(+), 323 deletions(-) delete mode 100755 scripts/check-submission.R delete mode 100644 templates/.Rbuildignore delete mode 100644 templates/.gitignore delete mode 100644 templates/DESCRIPTION delete mode 100644 templates/NEWS.md delete mode 100644 templates/inst/CITATION diff --git a/AGENTS.md b/AGENTS.md index f54ba0e..68a93a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,7 @@ not say "Bioconductor" explicitly but the package clearly targets it. ## Pre-submission gate (hard requirements for a new package) Do not tell a user their package is submission-ready unless all hold: - `R CMD check` clean on current R-devel (no errors, no warnings). -- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package' = TRUE)` clean. - Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; < 8 GB memory for vignettes/examples/tests. - `Version: 0.99.0`; `biocViews` present; a vignette and man pages present; valid maintainer @@ -49,10 +49,25 @@ acceptance, register an SSH key at BiocCredentials, add `upstream = git.biocondu push to both remotes; only `devel` and `RELEASE_x_y` branches accept pushes. Full sequence: `knowledge/workflow.md`. -## Optional helpers -- `scripts/check-submission.R` runs `R CMD check` + `BiocCheck` and reports pass/fail vs the gate - (needs R + BiocCheck installed). -- `templates/` holds lean skeletons (DESCRIPTION, NEWS.md, .Rbuildignore, .gitignore, - inst/CITATION). For full scaffolding, prefer the `biocthis` package. +## Tooling (use these, do not reimplement them) +This repo ships no validator and no templates on purpose - Bioconductor already maintains both, +and reusing existing infrastructure is itself a review criterion (ch 5). + +```r +# Validation - BiocCheck is authoritative +BiocCheck::BiocCheckGitClone() +BiocCheck::BiocCheck('new-package' = TRUE) + +# Scaffolding - biocthis writes Bioconductor-shaped files +biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_news_md() +biocthis::use_bioc_vignette(name = "", title = "Introduction to ") +biocthis::use_bioc_citation() +biocthis::use_bioc_github_action() +``` + +Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck cannot measure the two +timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those +need a real build. Canonical guide: https://contributions.bioconductor.org (source: github.com/Bioconductor/pkgrevdocs). diff --git a/README.md b/README.md index 08f9cbf..16392c3 100644 --- a/README.md +++ b/README.md @@ -21,27 +21,37 @@ bioc_package_dev/ ├── LICENSE # Apache-2.0 ├── knowledge/ # single source of truth - portable markdown summaries │ ├── index.md # topic router across all summaries -│ ├── workflow.md # end-to-end submission runbook -│ ├── SOURCES.md # slug->file map + pinned upstream commit (for refresh) +│ ├── workflow.md # end-to-end runbook, incl. converting an existing package +│ ├── SOURCES.md # .Rmd -> slug -> file map + pins for all tracked upstreams │ ├── 01-submissions.md # ch 1 │ ├── development/ # ch 2-23 (naming, metadata, docs, data, tests, code, ...) │ ├── maintenance.md # ch 24-30 │ ├── reviewer.md # ch 31-33 │ └── appendices.md # A-H -├── templates/ # lean package skeletons -├── scripts/ -│ └── check-submission.R # runs the pre-submission gate checks +├── docs/REFRESH.md # how to re-sync with upstream when it moves +├── scripts/ # verification, not package tooling (see "How this is verified") +├── evals/ # behavioral test cases for the skill and agent +├── .github/workflows/ # CI running the verification layers ├── .claude-plugin/ # Claude Code plugin + marketplace manifests ├── skills/bioconductor-package-dev/SKILL.md └── agents/bioc-package-review.md ``` +There is deliberately no templates directory and no check script. Bioconductor already +maintains both - `biocthis` for scaffolding, `BiocCheck` for validation - and reusing existing +infrastructure instead of reinventing it is one of the things reviewers look for (ch 5). Shipping +a competing copy would have made this repo violate the guidance it teaches. + ## Prerequisites For the guidance itself, none - it is markdown. To actually build and check a package you need: - R (current release, plus R-devel for the final submission check). -- `BiocManager`, and `BiocManager::install("BiocCheck")`. -- Optional: `biocthis` for full package scaffolding; `usethis`, `devtools`, `roxygen2`. +- `BiocManager::install(c("BiocCheck", "biocthis"))` - BiocCheck validates, biocthis scaffolds. + These are the two tools the skill and the agent tell you to run. +- Optional: `usethis`, `devtools`, `roxygen2`. + +To run this repo's own verification you need Python 3 (stdlib only) for `scripts/verify.py`; the +network layer additionally needs outbound HTTPS. No R is required for the static layer. ## Install and use @@ -106,12 +116,61 @@ demand. The summaries are stamped with the date they were generated from the live guide, and `knowledge/SOURCES.md` pins the exact upstream `pkgrevdocs` commit they came from. Bioconductor -updates the guide roughly twice a year with each release. Follow `context/REFRESH.md`: diff the +updates the guide roughly twice a year with each release. Follow `docs/REFRESH.md`: diff the current upstream commit against the pinned one, regenerate only the changed chapters, bump `version` in `.claude-plugin/plugin.json`, and push. Marketplace users then run `/plugin marketplace update` and `/plugin update bioconductor-package-dev`. The summaries always link back to the canonical chapter, which is the authority if anything drifts. +Five upstreams are tracked, not just the guide: `pkgrevdocs`, the Contributions issue template, +BiocCheck, biocthis, and bioc-actions. All five pins live in `knowledge/SOURCES.md`, and the +weekly `fidelity` CI job opens an issue when any of them moves. + +## How this is verified + +Prose summarizing a live document rots quietly: a URL dies, upstream rewords a rule, a threshold +drifts between the files that restate it, or a reworded skill `description` stops the skill from +firing while every file still looks fine. Four layers catch different failures. + +| Layer | Command | Catches | +|---|---|---| +| Static | `python3 scripts/verify.py` | broken internal paths, gitignored references, manifest and frontmatter errors, missing `Source:`/`Fetched` stamps, rule text that has drifted between the files that duplicate it, emoji, a stale README tree | +| Fidelity | `python3 scripts/verify.py --network` | upstream commit drift mapped to the affected summaries, dead chapter URLs, thresholds that no longer match upstream, summaries that harden an upstream "should" into a "must", gate wording that no longer matches the submission tracker, new upstream chapters nobody summarized | +| Golden path | `Rscript scripts/golden-path.R` + `R CMD build` + `BiocCheck` in CI | the scaffolding commands this repo tells you to run, by running them and checking the result with real Bioconductor tooling | +| Behavior | `claude plugin eval .` | the skill firing when it should, staying quiet when it should not, and returning the right values - including every prompt in "Example prompts" above | + +The fidelity job is a weekly cron rather than a PR gate: upstream changing is a reason to open an +issue, not to block someone's pull request. + +## Built on + +This repo is a thin layer over other people's work. It contributes routing, summarization, and +verification; everything substantive below belongs to the projects listed here, and the design +rule throughout has been to point at existing Bioconductor infrastructure rather than ship a +competing copy of it. + +- **Bioconductor Packages: Development, Maintenance, and Peer Review** - Kevin Rue-Albrecht, + Daniela Cassol, Johannes Rainer, Lori Shepherd, Marcel Ramos Pérez, Martin Morgan. + https://contributions.bioconductor.org, source + [Bioconductor/pkgrevdocs](https://github.com/Bioconductor/pkgrevdocs). Every file under + `knowledge/` is derived from it, cites the chapter it came from, and defers to it on any + disagreement. +- **[BiocCheck](https://github.com/Bioconductor/BiocCheck)** - Lori Shepherd, Marcel Ramos, and + the Bioconductor core team. The authoritative validator. This repo runs it and reads its output + instead of reimplementing its checks. +- **[biocthis](https://github.com/lcolladotor/biocthis)** - Leonardo Collado-Torres. Scaffolding + that writes Bioconductor-shaped package files. This repo recommends it rather than shipping + templates, and CI runs the exact command sequence it recommends. +- **[bioc-actions](https://github.com/grimbough/bioc-actions)** - Mike Smith. Composite GitHub + Actions for setting up Bioconductor, building, checking, and running BiocCheck. They power the + golden-path job, which is what lets this repo claim its instructions actually work. +- **[Bioconductor/Contributions](https://github.com/Bioconductor/Contributions)** - the submission + tracker, and the source of the authoritative pre-submission checklist that the gate here is + checked against. + +Thanks to all of them. Errors in the summaries are this repo's, not theirs - report them here, and +consult the linked chapter as the authority. + ## Attribution and license The summaries are derived from the Bioconductor contribution guide, source repository diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index 85490be..8b93cc6 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -1,11 +1,12 @@ --- name: bioc-package-review description: >- - Audit an R package for Bioconductor submission readiness. Use when the user asks to review, - audit, or check whether their package is ready to submit to Bioconductor, or to run a - pre-submission check. Walks the package against the official pre-submission gate and the - contribution guidelines, then returns a structured blockers/warnings/suggestions report mapped - to specific guide chapters. + Audits a package against Bioconductor's submission requirements and reports what would block + acceptance, before a reviewer sees it. Use when the user asks to review, audit, or check whether + their package is ready to submit, asks what a reviewer would flag, or wants a pre-submission + check. Walks the package against the official pre-submission gate and the contribution + guidelines, then returns a structured blockers/warnings/suggestions report mapped to specific + guide chapters and ending in a verdict. Read-only: it renders a verdict, it does not fix. model: fable tools: Read, Grep, Glob, Bash --- @@ -48,15 +49,38 @@ rather than reinventing them. ## The gate (report each as pass/fail) - `R CMD check` clean on R-devel (no errors, no warnings). - `BiocCheck::BiocCheckGitClone()` clean. -- `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- `BiocCheck::BiocCheck('new-package' = TRUE)` clean. - Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; files <= 5 MB; < 8 GB memory. - `Version: 0.99.0`; `biocViews`, vignette, man pages present; valid maintainer; not on CRAN; hosted on GitHub default branch. -If R and BiocCheck are installed, you may run `${CLAUDE_PLUGIN_ROOT}/scripts/check-submission.R` -(or `R CMD check` + `BiocCheck::BiocCheck()` directly) from the package root and parse the output. -If R is unavailable, say so and audit statically from the files, marking the check items as -"not run - R unavailable". +## Tooling (use these, do not reimplement them) +This repo ships no validator and no templates on purpose - Bioconductor already maintains both, +and reusing existing infrastructure is itself a review criterion (ch 5). + +```r +# Validation - BiocCheck is authoritative +BiocCheck::BiocCheckGitClone() +BiocCheck::BiocCheck('new-package' = TRUE) + +# Scaffolding - biocthis writes Bioconductor-shaped files +biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_news_md() +biocthis::use_bioc_vignette(name = "", title = "Introduction to ") +biocthis::use_bioc_citation() +biocthis::use_bioc_github_action() +``` + +Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck cannot measure the two +timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those +need a real build. + +Run the two BiocCheck calls via Bash from the package root when BiocCheck is installed, and parse +the output into your findings. When BiocCheck is unavailable, mark those gate items "not run - +BiocCheck unavailable" and audit statically from the files instead. Report the two timing items as +"requires a build" rather than claiming a verdict on them; do not run `R CMD check` yourself unless +the user asks, since a full check can take many minutes. Never guess a gate result you did not +measure. ## Report format Return a structured report, most severe first: diff --git a/knowledge/01-submissions.md b/knowledge/01-submissions.md index 412ab82..47af4a0 100644 --- a/knowledge/01-submissions.md +++ b/knowledge/01-submissions.md @@ -1,17 +1,28 @@ # Covers: Chapter 1 - Bioconductor package submission overview, eligibility, package types, and submission mechanics. -## Eligibility (must all hold for Software packages) - -- Package must address high-throughput genomic analysis (sequencing, microarrays, - flow cytometry, mass spectrometry, image analysis, or similar biological data). -- Must interoperate with other Bioconductor packages by re-using common - Bioconductor data structures (do not reinvent existing classes). -- Must NOT already exist on CRAN (CRAN and Bioconductor are mutually exclusive). -- All dependencies must be available on CRAN or Bioconductor. -- Must include full documentation and evaluated (runnable) vignettes. -- Must comply with the Bioconductor Package Guidelines. -- Maintainer commits to long-term support via the Bioconductor support site. -- Follow software best practices supporting reproducible research. +## Eligibility + +Upstream states this list as "To submit a package to Bioconductor the package **should**" - these +are review criteria, not a mechanical pass/fail gate. Some individual items carry harder modality +than the list stem, and that difference is preserved below. + +- Should address areas of high-throughput genomic analysis (sequencing, expression and other + microarrays, flow cytometry, mass spectrometry, image analysis); see biocViews. +- Should interoperate with other Bioconductor packages by re-using common data structures and + existing infrastructure (e.g. `rtracklayer::import()` for common genomic file input) rather + than reinventing them. +- Should adopt software best practices enabling reproducible research: full documentation and + fully evaluated vignettes, plus commitment to long-term user support on the support site. +- **Cannot** exist on CRAN - "A package can only be submitted to one or the other." +- **Cannot** depend on any package, or version of a package, not yet available on CRAN or + Bioconductor; it should work with the current publicly available version. +- Should comply with the Package Guidelines. + +Note on scope: the genomic-analysis criterion is a "should", and Bioconductor does host accepted +Software packages that perform no genomic analysis themselves (BiocCheck, biocthis, BiocStyle are +developer infrastructure). A tool outside classic genomics is therefore a judgement call for the +reviewers, not an automatic rejection - but the burden is on the submitter to argue the fit, and +this is a good thing to raise in the submission issue rather than discover during review. ## Package types @@ -29,10 +40,13 @@ ## Submission mechanics (Software / Experiment Data) - Host the package in a GitHub repository. -- The package must live on the repository's DEFAULT branch, and that branch must - contain ONLY package code. GitHub Actions / devtools / CI helper files must be - kept on separate (non-default) branches. -- Package name must not conflict (case-insensitive) with any current or past +- The package must live on the repository's DEFAULT branch - you cannot specify an + alternative branch - and that branch "must contain only package code". Files or + directories for other applications (GitHub Actions, devtools, etc) "should be in + a different branch". Upstream states the first as a requirement and the second as + a recommendation; do not report a package as non-compliant solely for carrying a + CI workflow on the default branch. +- Package name should not conflict (case-insensitive) with any current or past Bioconductor or CRAN package. The contributor grants Bioconductor rights to the package name (CRAN-style naming/ownership policy applies). - Open a NEW issue on the tracker at github.com/Bioconductor/Contributions with @@ -84,4 +98,4 @@ documentation review. Source: https://contributions.bioconductor.org/bioconductor-package-submissions.html (and overview: https://contributions.bioconductor.org/submission-overview.html) -Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). +Fetched 2026-08-14 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/SOURCES.md b/knowledge/SOURCES.md index 5f2b5e8..81b2c16 100644 --- a/knowledge/SOURCES.md +++ b/knowledge/SOURCES.md @@ -1,59 +1,108 @@ # Sources and refresh baseline This file records where each summary came from and the exact upstream state it was generated -against. Use it to detect drift and to refresh only what changed. See `context/REFRESH.md` for -the procedure. +against. Use it to detect drift and to refresh only what changed. `docs/REFRESH.md` is the +procedure; this file is the data. `scripts/verify.py --network` reads both. -## Upstream baseline (update on every refresh) +## Tracked upstreams (update every pin on every refresh) + +Five upstreams, not one. A change to any of them can silently invalidate the guidance here. + +| Upstream | Pin | Verified | Invalidates | +|---|---|---|---| +| [Bioconductor/pkgrevdocs](https://github.com/Bioconductor/pkgrevdocs) (`devel`) | `9b078ea2a0ec05274be83cb12ea75473c6d0c808` (committed 2026-07-20) | 2026-08-14 | all of `knowledge/` | +| [Bioconductor/Contributions](https://github.com/Bioconductor/Contributions) `issue_template.md` | `d2631e3da63092937a96d476c6f7fb915a168069` (committed 2021-07-12) | 2026-08-14 | the pre-submission gate wording in `AGENTS.md`, `SKILL.md`, `agents/bioc-package-review.md` | +| [Bioconductor/BiocCheck](https://github.com/Bioconductor/BiocCheck) | release 1.48.1, devel 1.49.30 | 2026-08-14 | what "BiocCheck clean" means; what the review agent should pre-empt | +| [lcolladotor/biocthis](https://github.com/lcolladotor/biocthis) | release 1.22.0, devel 1.23.0 | 2026-08-14 | the scaffolding block in the three router files, and `scripts/golden-path.R` | +| [grimbough/bioc-actions](https://github.com/grimbough/bioc-actions) | `v1.0.16` (`455bb7a12b1f0df041fc1078de581d2c508839d9`) | 2026-08-14 | `.github/workflows/verify.yml` | + +Bioconductor cycle at the last refresh: release 3.23, devel 3.24, both on R 4.6.0 +(source: https://bioconductor.org/config.yaml). + +Other baseline facts: -- Source repository: https://github.com/Bioconductor/pkgrevdocs -- Default branch: `devel` -- Pinned commit (last summarized from): `9b078ea2a0ec05274be83cb12ea75473c6d0c808` -- Commit date: 2026-07-20 -- Summaries fetched: 2026-07-23 - Rendered guide: https://contributions.bioconductor.org +- Summaries fetched: 2026-08-14 (files edited on that date carry that `Fetched` stamp; files + untouched since the previous pass still carry 2026-07-23 and are still accurate, because the + pkgrevdocs pin has not moved between the two dates) + +Machine-readable endpoints used for drift detection: + +- pkgrevdocs commit: `https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel`, field `sha` +- changed files since the pin: `https://github.com/Bioconductor/pkgrevdocs/compare/...devel` +- chapter order: `https://raw.githubusercontent.com/Bioconductor/pkgrevdocs/devel/_bookdown.yml` +- BiocCheck / biocthis versions: `https://bioconductor.org/packages/release/bioc/VIEWS` and + `.../devel/bioc/VIEWS` (parse `Package:` / `Version:` pairs - the HTML landing pages are not + reliably parseable) +- bioc-actions tags: `https://api.github.com/repos/grimbough/bioc-actions/tags` + +## Chapter map: .Rmd source -> rendered slug -> knowledge file + +Two columns are needed, not one. The GitHub compare API reports changed **`.Rmd` filenames**, +while every `Source:` footer in `knowledge/` cites a **rendered slug** - and they routinely differ +(`package-maintainence.Rmd` renders to `package-maintenance.html`, note the upstream misspelling; +`bioc-classes-methods.Rmd` renders to `reusebioc.html`). A drift report can only be scoped to the +right files by joining through this table. + +Rendered pages live at `https://contributions.bioconductor.org/.html`. Chapter order and +`.Rmd` names come from `_bookdown.yml`; slugs come from each chapter's `{#id}` anchor. + +| .Rmd | Slug | Ch | Knowledge file | +|---|---|---|---| +| `index.Rmd` | `index` | - | not summarized (welcome page, no rules) | +| `package-submission.Rmd` | `submission-overview` | - | `01-submissions.md` | +| `package-submission.Rmd` | `bioconductor-package-submissions` | 1 | `01-submissions.md` | +| `devguide-introduction.Rmd` | `develop-overview` | - | not summarized (part overview, no rules) | +| `package-name.Rmd` | `package-name` | 2 | `development/package-name.md` | +| `general-package-development.Rmd` | `general` | 3 | `development/general-dev.md` | +| `important-bioc-features.Rmd` | `important-bioconductor-package-development-features` | 4 | `development/general-dev.md` | +| `bioc-classes-methods.Rmd` | `reusebioc` | 5 | `development/methods-classes.md` | +| `readme-file.Rmd` | `readme` | 6 | `development/metadata-files.md` | +| `description-file.Rmd` | `description` | 7 | `development/metadata-files.md` | +| `namespace-file.Rmd` | `namespace` | 8 | `development/metadata-files.md` | +| `news-file.Rmd` | `news` | 9 | `development/metadata-files.md` | +| `license-file.Rmd` | `license` | 10 | `development/metadata-files.md` | +| `citation-file.Rmd` | `citation` | 11 | `development/metadata-files.md` | +| `install-file.Rmd` | `sysdep` | 12 | `development/metadata-files.md` | +| `documentation.Rmd` | `docs` | 13 | `development/documentation.md` | +| `package-data.Rmd` | `data` | 14 | `development/data.md` | +| `unit-tests.Rmd` | `tests` | 15 | `development/tests.md` | +| `r-code.Rmd` | `r-code` | 16 | `development/r-code.md` | +| `fortran-C-python.Rmd` | `other-than-Rcode` | 17 | `development/compiled-thirdparty.md` | +| `shiny-apps.Rmd` | `shiny` | 18 | `development/shiny.md` | +| `ai-policy-third-party.Rmd` | `ai-policy-third-party` | 19 | `development/ai-policy.md` | +| `non-software-packages.Rmd` | `non-software` | 20 | `development/non-software-pkgs.md` | +| `gitignore-file.Rmd` | `gitignore` | 21 | `development/gitignore.md` | +| `build-check-bioccheck.Rmd` | `build-check-bioccheck` | 22 | `development/build-check-bioccheck.md` | +| `devguide-conclusion.Rmd` | `conclusion` | 23 | `development/build-check-bioccheck.md` | +| `package-maintainence.Rmd` | `package-maintenance` | - | not summarized (part overview, no rules) | +| `git-version-control.Rmd` | `git-version-control` | 24 | `maintenance.md` | +| `version-numbering.Rmd` | `versionnum` | 25 | `maintenance.md` | +| `troubleshoot-build-report.Rmd` | `troubleshooting-build-report` | 26 | `maintenance.md` | +| `debugging-c-code.Rmd` | `debugging-cc-code` | 27 | `maintenance.md` | +| `deprecation.Rmd` | `deprecation` | 28 | `maintenance.md` | +| `package-end-of-life.Rmd` | `package-end-of-life-policy` | 29 | `maintenance.md` | +| `branch-rename-faq.Rmd` | `branch-rename-faqs` | 30 | `maintenance.md` | +| `review-overview.Rmd` | `reviewer-resources-overview` | - | `reviewer.md` | +| `review-expectations.Rmd` | `review-expectation` | 31 | `reviewer.md` | +| `review-resources-and-tools.Rmd` | `reviewtools` | 32 | `reviewer.md` | +| `volunteer-to-review.Rmd` | `review-volunteer-chapter` | 33 | `reviewer.md` | +| `appendix.Rmd` | - | - | no page (part marker only) | +| `devel-branch.Rmd` | `use-devel` | A | `appendices.md` | +| `advanced-build-options.Rmd` | `advanced-build-options` | B | `appendices.md` | +| `web-query.Rmd` | `querying-web-resources` | C | `appendices.md` | +| `c-and-fortran.Rmd` | `c-fortran` | D | `appendices.md` | +| `mavericks.Rmd` | `cmavericks-best-practices` | E | `appendices.md` | +| `debug-rd-links.Rmd` | `man-links` | F | `appendices.md` | +| `news-for-bookdown.Rmd` | `booknews` | G | `appendices.md` | +| `references.Rmd` | `references-1` | H | `appendices.md` | + +Three slugs are deliberately marked "not summarized": `index`, `develop-overview` and +`package-maintenance` are welcome or part-overview pages whose entire body is a sentence or two of +orientation. They carry no rules to summarize. They are listed anyway so the chapter-coverage +check in `verify.py --network` can tell "we decided not to summarize this" apart from "we missed +a chapter". + +## Not chapter-derived (maintain by hand) -To find the current upstream commit: -`https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel` (field `sha`). Compare the -`sha` to the pinned commit above. If they differ, list changed chapter files with the GitHub -compare API: `https://github.com/Bioconductor/pkgrevdocs/compare/...devel` - -## Chapter slug -> knowledge file map - -Every page is `https://contributions.bioconductor.org/.html`. - -Submissions: -- submission-overview, bioconductor-package-submissions (ch1) -> `01-submissions.md` - -Development (ch 2-23) -> `development/`: -- package-name (ch2) -> `development/package-name.md` -- general (ch3), important-bioconductor-package-development-features (ch4) -> `development/general-dev.md` -- reusebioc (ch5) -> `development/methods-classes.md` -- readme (ch6), description (ch7), namespace (ch8), news (ch9), license (ch10), citation (ch11), - sysdep (ch12) -> `development/metadata-files.md` -- docs (ch13) -> `development/documentation.md` -- data (ch14) -> `development/data.md` -- tests (ch15) -> `development/tests.md` -- r-code (ch16) -> `development/r-code.md` -- other-than-Rcode (ch17) -> `development/compiled-thirdparty.md` -- shiny (ch18) -> `development/shiny.md` -- ai-policy-third-party (ch19) -> `development/ai-policy.md` -- non-software (ch20) -> `development/non-software-pkgs.md` -- gitignore (ch21) -> `development/gitignore.md` -- build-check-bioccheck (ch22), conclusion (ch23) -> `development/build-check-bioccheck.md` - -Maintenance (ch 24-30) -> `maintenance.md`: -- package-maintenance, git-version-control (ch24), versionnum (ch25), - troubleshooting-build-report (ch26), debugging-cc-code (ch27), deprecation (ch28), - package-end-of-life-policy (ch29), branch-rename-faqs (ch30) - -Reviewer (ch 31-33) -> `reviewer.md`: -- reviewer-resources-overview, review-expectation (ch31), reviewtools (ch32), - review-volunteer-chapter (ch33) - -Appendices (A-H) -> `appendices.md`: -- use-devel (A), advanced-build-options (B), querying-web-resources (C), c-fortran (D), - cmavericks-best-practices (E), man-links (F), booknews (G), references-1 (H) - -Not chapter-derived (maintain by hand): -- `index.md` (router), `workflow.md` (runbook), this file. +`index.md` (router), `workflow.md` (runbook), and this file. diff --git a/knowledge/workflow.md b/knowledge/workflow.md index ea781eb..a22818e 100644 --- a/knowledge/workflow.md +++ b/knowledge/workflow.md @@ -3,6 +3,40 @@ The sequential path from local package to first Bioconductor release. The per-chapter files under this directory are reference; this file is the process. Follow it top to bottom. +## Converting an existing package (start here if code already exists) + +Most submitters are not starting from an empty directory - they have a working package or tool on +GitHub. Do not scaffold from scratch. Work the list below in order; it is ordered by how expensive +the problem is to discover late, not by how hard it is to fix. + +1. **Eligibility and type** - Phase 0 below. Cheapest to answer and the only one that can end the + effort entirely. +2. **CRAN status** - "Not exist on CRAN. A package can only be submitted to one or the other." + Moving from CRAN means leaving CRAN, not dual-listing. Note also that the naming policy says a + name should not conflict with "any current or past CRAN package"; a maintainer migrating their + own package should raise that in the submission issue rather than assume it is fine. +3. **Name** - see `development/package-name.md`. Renaming after review has started is painful, and + the check is a search, not a build. +4. **Version** - reset to `0.99.0` no matter what the package is at today. A package at `2.4.1` on + GitHub still submits as `0.99.0`. See `maintenance.md`. +5. **Metadata gaps** - `biocViews` (usually missing entirely on a non-Bioc package), `Authors@R` + with a valid `cre` email, `NEWS.md`, `inst/CITATION`. `biocthis::use_bioc_description()` and + friends write these; see the tooling block in `AGENTS.md`. +6. **Reuse audit** - does the package define its own container where `SummarizedExperiment`, + `GRanges`, or another core class would do? This is the single most common substantive review + request and the most expensive to retrofit. See `development/methods-classes.md` (ch 5). +7. **Data placement** - anything large moves out of the package to ExperimentHub/AnnotationHub + before you measure sizes. See `development/data.md`. +8. **Documentation** - a real evaluated vignette, not a stub; man pages with runnable examples. + Existing packages usually have a README doing the vignette's job. See + `development/documentation.md`. +9. **Code style** - `<-`, 4-space indent, 80 columns, no `1:n`. Mechanical, so do it last; doing it + first only creates conflicts with the changes above. +10. **Run the gate** - Phase 1 below, then Phase 2 onward unchanged. + +Steps 1-5 are usually a day. Step 6 is where a conversion either goes smoothly or becomes a +rewrite, so check it early even though it is fixed late. + ## Phase 0 - Decide it belongs in Bioconductor - Package addresses high-throughput genomic / biological data analysis. - Reuses standard Bioconductor data structures (e.g. SummarizedExperiment, S4) where possible. @@ -20,13 +54,14 @@ Hard gate (all required): - Valid maintainer email; maintainer == the person who will submit. - `R CMD check` clean on current R-devel (no errors, no warnings). - `BiocCheck::BiocCheckGitClone()` clean. -- `BiocCheck::BiocCheck('new-package'=TRUE)` clean (no errors, no warnings). +- `BiocCheck::BiocCheck('new-package' = TRUE)` clean (no errors, no warnings). - Source build < 10 MB (`R CMD build`); `R CMD check --no-build-vignettes` < 10 min. - Every individual file <= 5 MB; running vignettes/examples/tests uses < 8 GB memory. - Bioc code style in R code: `<-`, 4-space indent, 80-col. See `development/r-code.md`. Use the current devel Bioconductor with the matching R version - see `appendices.md` (Appendix A) -and `development/general-dev.md`. Optional helper: `scripts/check-submission.R` runs the checks. +and `development/general-dev.md`. BiocCheck is the authoritative validator for everything above +except the two timing items, which need a real build; see the tooling block in `AGENTS.md`. ## Phase 2 - Host on GitHub - Push the package to the DEFAULT branch of a public GitHub repository (not a subdirectory, @@ -78,4 +113,4 @@ Once accepted (detail: `maintenance.md`, ch 24): Source: https://contributions.bioconductor.org/bioconductor-package-submissions.html, https://contributions.bioconductor.org/git-version-control.html, https://contributions.bioconductor.org/versionnum.html -Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). +Fetched 2026-08-14 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/scripts/check-submission.R b/scripts/check-submission.R deleted file mode 100755 index 974de26..0000000 --- a/scripts/check-submission.R +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env Rscript - -# check-submission.R -# Run the Bioconductor pre-submission gate against a package and report pass/fail. -# Usage: -# Rscript check-submission.R [path/to/package] -# Defaults to the current directory. Requires R-devel plus the BiocCheck package. -# This is a convenience wrapper; the authoritative checks are R CMD check and BiocCheck. - -args <- commandArgs(trailingOnly = TRUE) -pkg_dir <- if (length(args) >= 1) args[[1]] else "." -pkg_dir <- normalizePath(pkg_dir, mustWork = TRUE) - -desc_path <- file.path(pkg_dir, "DESCRIPTION") -if (!file.exists(desc_path)) { - stop("No DESCRIPTION found in '", pkg_dir, "'. Point this at a package root.") -} - -pass <- character(0) -fail <- character(0) -note <- character(0) -record <- function(ok, msg) { - if (isTRUE(ok)) pass[[length(pass) + 1]] <<- msg - else fail[[length(fail) + 1]] <<- msg -} - -dcf <- read.dcf(desc_path) -field <- function(name) if (name %in% colnames(dcf)) trimws(dcf[1, name]) else NA_character_ - -# --- Static gate checks (no build required) --- -version <- field("Version") -record(!is.na(version) && grepl("^0\\.99\\.[0-9]+$", version), - sprintf("Version is 0.99.z for a new package (found: %s)", version)) - -record(!is.na(field("biocViews")) && nzchar(field("biocViews")), - "biocViews field present") - -maint <- field("Authors@R") -if (is.na(maint) || !nzchar(maint)) maint <- field("Maintainer") -record(!is.na(maint) && grepl("[[:alnum:]._%+-]+@[[:alnum:].-]+", maint), - "Maintainer with an email present") - -record(dir.exists(file.path(pkg_dir, "vignettes")), - "vignettes/ directory present") - -record(dir.exists(file.path(pkg_dir, "man")), - "man/ directory present") - -record(dir.exists(file.path(pkg_dir, "tests")), - "tests/ directory present") - -# Individual file size <= 5 MB -all_files <- list.files(pkg_dir, recursive = TRUE, full.names = TRUE, all.files = TRUE, no.. = TRUE) -big <- all_files[file.size(all_files) > 5 * 1024^2] -big <- big[!grepl("(^|/)\\.git/", big)] -record(length(big) == 0, - if (length(big) == 0) "No individual file exceeds 5 MB" - else sprintf("Files exceed 5 MB: %s", paste(basename(big), collapse = ", "))) - -# --- Dynamic checks (BiocCheck), if available --- -if (requireNamespace("BiocCheck", quietly = TRUE)) { - message("Running BiocCheck::BiocCheck('new-package' = TRUE) ...") - res <- tryCatch( - BiocCheck::BiocCheck(pkg_dir, `new-package` = TRUE), - error = function(e) { message("BiocCheck errored: ", conditionMessage(e)); NULL } - ) - if (!is.null(res)) { - n_err <- tryCatch(length(res$error), error = function(e) NA_integer_) - n_warn <- tryCatch(length(res$warning), error = function(e) NA_integer_) - record(isTRUE(n_err == 0), - sprintf("BiocCheck errors: %s", ifelse(is.na(n_err), "unknown", n_err))) - record(isTRUE(n_warn == 0), - sprintf("BiocCheck warnings: %s", ifelse(is.na(n_warn), "unknown", n_warn))) - } -} else { - note[[length(note) + 1]] <- paste( - "BiocCheck not installed - skipped R CMD check / BiocCheck. Install with", - "BiocManager::install('BiocCheck'), then run R CMD check and BiocCheck manually.") -} - -# --- Report --- -cat("\n=== Bioconductor pre-submission gate ===\n") -cat("Package:", field("Package"), "at", pkg_dir, "\n\n") -if (length(pass)) cat("PASS:\n", paste0(" - ", pass, collapse = "\n"), "\n", sep = "") -if (length(fail)) cat("\nFAIL:\n", paste0(" - ", fail, collapse = "\n"), "\n", sep = "") -if (length(note)) cat("\nNOTES:\n", paste0(" - ", note, collapse = "\n"), "\n", sep = "") - -cat("\nReminder: this wrapper does not replace `R CMD check` on R-devel. Run it, plus\n") -cat("BiocCheck::BiocCheckGitClone(), before opening a submission issue.\n") - -quit(status = if (length(fail) > 0) 1L else 0L) diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index a3f0fa5..b57c416 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -1,12 +1,14 @@ --- name: bioconductor-package-dev description: >- - Use when developing, maintaining, submitting, or reviewing a Bioconductor R package. Fires on - Bioconductor/Bioc package work, DESCRIPTION/NAMESPACE/NEWS/biocViews/BiocCheck, submission prep, - the Bioconductor Contributions tracker or git.bioconductor.org, and S4 or Bioconductor core - classes such as SummarizedExperiment - even when the user does not say "Bioconductor" explicitly - but the package clearly targets it. Provides the official contribution rules, the pre-submission - gate, version numbering, and a topic router into curated per-chapter summaries. + Guides you through turning an existing R package into a Bioconductor submission - what the + requirements are, what the pre-submission gate demands, and how peer review works. Use when a + package on GitHub is being prepared for Bioconductor, when moving a package from CRAN to + Bioconductor, when asked whether a package is submission-ready, and for any Bioconductor + development, maintenance, or review work: DESCRIPTION/NAMESPACE/NEWS/biocViews/BiocCheck, + version numbering, vignettes and man pages, large data placement, the Contributions tracker, + git.bioconductor.org, and S4 or Bioconductor core classes such as SummarizedExperiment - even + when the user does not say "Bioconductor" explicitly but the package clearly targets it. --- # Bioconductor package development @@ -15,51 +17,57 @@ Curated from the official guide "Bioconductor Packages: Development, Maintenance Review" (https://contributions.bioconductor.org). The detailed summaries live in `${CLAUDE_PLUGIN_ROOT}/knowledge/` - open the file for the task instead of loading everything. -## How to use this skill -1. Identify the lifecycle stage: Authoring, Submission, Maintenance, or Review. -2. Open the matching `knowledge/` file (map below) for the rules and exact values. -3. Apply the cross-cutting rules here (gate, version, style) to whatever you write or check. -4. For an end-to-end submission, follow `${CLAUDE_PLUGIN_ROOT}/knowledge/workflow.md`. - -## Router (open the file that matches the task) -- Whole submission path, start to finish: `knowledge/workflow.md` -- Full topic map: `knowledge/index.md` -- Submission mechanics + package types: `knowledge/01-submissions.md` -- Naming: `knowledge/development/package-name.md` -- General dev + key features: `knowledge/development/general-dev.md` -- Reusing Bioc classes/methods (S4, SummarizedExperiment): `knowledge/development/methods-classes.md` -- Metadata files (README/DESCRIPTION/NAMESPACE/NEWS/LICENSE/CITATION/INSTALL): - `knowledge/development/metadata-files.md` -- Documentation (vignettes, man pages): `knowledge/development/documentation.md` -- Package data + large data: `knowledge/development/data.md` -- Unit tests: `knowledge/development/tests.md` -- R code + code style: `knowledge/development/r-code.md` -- Compiled / third-party code: `knowledge/development/compiled-thirdparty.md` -- Shiny: `knowledge/development/shiny.md` -- AI policy + third-party code: `knowledge/development/ai-policy.md` -- Non-software packages: `knowledge/development/non-software-pkgs.md` -- .gitignore: `knowledge/development/gitignore.md` -- Build / Check / BiocCheck (the gate): `knowledge/development/build-check-bioccheck.md` -- Maintenance (git server, versioning, build reports, deprecation): `knowledge/maintenance.md` -- What reviewers check: `knowledge/reviewer.md` -- Appendices (devel Bioc, build options, C/Fortran, etc.): `knowledge/appendices.md` +The common case is conversion: the user already has a working package or tool on GitHub and wants +it in Bioconductor. Start from what exists and find the gaps against the gate; do not scaffold +from scratch unless there is no package yet. -## Cross-cutting rules (apply always) +## How to use this skill +1. Identify the lifecycle stage: Authoring, Conversion/Submission, Maintenance, or Review. +2. Open the matching `knowledge/` file for the rules and exact values. The topic router lives in + `${CLAUDE_PLUGIN_ROOT}/AGENTS.md` under "Router", and the full map in `knowledge/index.md`. +3. Apply the cross-cutting rules below (gate, version, style) to whatever you write or check. +4. For an end-to-end submission, follow `${CLAUDE_PLUGIN_ROOT}/knowledge/workflow.md`. For an + existing package, start at its "Converting an existing package" section. -Pre-submission gate - a new package is NOT submission-ready unless all hold: +## Pre-submission gate (hard requirements for a new package) +Do not tell a user their package is submission-ready unless all hold: - `R CMD check` clean on current R-devel (no errors, no warnings). -- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package'=TRUE)` clean. +- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package' = TRUE)` clean. - Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; < 8 GB memory for vignettes/examples/tests. - `Version: 0.99.0`; `biocViews` present; a vignette and man pages present; valid maintainer email; not on CRAN; hosted on the GitHub default branch. +Detail: `knowledge/development/build-check-bioccheck.md` and `knowledge/development/general-dev.md`. + +## Version rule +Start `0.99.0`. Scheme `x.y.z`: `y` odd in devel, even in release (max 99); bump `z` by 1 on +every commit; `0.99.z` becomes `1.0.0` at the first Bioconductor release; `x` changed only by +the Bioconductor team. Detail: `knowledge/maintenance.md`. -Version rule: start `0.99.0`; in `x.y.z`, `y` is odd in devel / even in release (max 99); bump -`z` by 1 every commit; `0.99.z` becomes `1.0.0` at the first Bioconductor release; `x` changed -only by the Bioconductor team. +## Bioconductor code style (differs from tidyverse) +Use `<-` for assignment, 4-space indentation, 80-column lines; prefer vectorized code; avoid +`1:n` (use `seq_len`/`seq_along`). Detail: `knowledge/development/r-code.md`. -Bioconductor code style (differs from tidyverse): `<-` assignment, 4-space indent, 80-column -lines; prefer vectorized code; avoid `1:n` (use `seq_len`/`seq_along`). +## Tooling (use these, do not reimplement them) +This repo ships no validator and no templates on purpose - Bioconductor already maintains both, +and reusing existing infrastructure is itself a review criterion (ch 5). + +```r +# Validation - BiocCheck is authoritative +BiocCheck::BiocCheckGitClone() +BiocCheck::BiocCheck('new-package' = TRUE) + +# Scaffolding - biocthis writes Bioconductor-shaped files +biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_news_md() +biocthis::use_bioc_vignette(name = "", title = "Introduction to ") +biocthis::use_bioc_citation() +biocthis::use_bioc_github_action() +``` + +Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck cannot measure the two +timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those +need a real build. ## Submission and git server (short) Host on the GitHub default branch, then open an issue (title = package name) at @@ -69,10 +77,4 @@ acceptance: register an SSH key at BiocCredentials, add `upstream = git.biocondu to both remotes; only `devel` and `RELEASE_x_y` branches accept pushes. Full sequence: `knowledge/workflow.md`. -## Optional helpers (in this plugin) -- `${CLAUDE_PLUGIN_ROOT}/scripts/check-submission.R` - runs `R CMD check` + `BiocCheck` and - reports pass/fail vs the gate (needs R + BiocCheck installed). -- `${CLAUDE_PLUGIN_ROOT}/templates/` - lean skeletons (DESCRIPTION, NEWS.md, .Rbuildignore, - .gitignore, inst/CITATION). For full scaffolding, prefer the `biocthis` package. - For a full submission-readiness audit, use the `bioc-package-review` agent. diff --git a/templates/.Rbuildignore b/templates/.Rbuildignore deleted file mode 100644 index 8190437..0000000 --- a/templates/.Rbuildignore +++ /dev/null @@ -1,13 +0,0 @@ -^.*\.Rproj$ -^\.Rproj\.user$ -^\.github$ -^\.git$ -^\.gitignore$ -^README\.Rmd$ -^LICENSE\.md$ -^\.Rbuildignore$ -^doc$ -^Meta$ -^\.lintr$ -^codecov\.yml$ -^\.covrignore$ diff --git a/templates/.gitignore b/templates/.gitignore deleted file mode 100644 index f001ed6..0000000 --- a/templates/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# R -.Rproj.user -.Rhistory -.RData -.Ruserdata -*.Rproj - -# Build artifacts -*.tar.gz -*.Rcheck/ -src/*.o -src/*.so -src/*.dll - -# Vignette / docs output -doc/ -Meta/ -inst/doc/ - -# OS -.DS_Store -Thumbs.db diff --git a/templates/DESCRIPTION b/templates/DESCRIPTION deleted file mode 100644 index 2b01c33..0000000 --- a/templates/DESCRIPTION +++ /dev/null @@ -1,29 +0,0 @@ -Package: MyBiocPackage -Type: Package -Title: One Line Title In Title Case Without A Trailing Period -Version: 0.99.0 -Authors@R: - person("Given", "Family", email = "you@example.com", - role = c("aut", "cre"), - comment = c(ORCID = "0000-0000-0000-0000")) -Description: One paragraph describing what the package does. Use several complete - sentences. Do not start with the package name or "This package". Explain the - biological problem and the methods provided. -License: Artistic-2.0 -Encoding: UTF-8 -biocViews: Software, GeneExpression, RNASeq -Imports: - methods, - S4Vectors, - SummarizedExperiment -Suggests: - knitr, - rmarkdown, - BiocStyle, - testthat (>= 3.0.0) -VignetteBuilder: knitr -Depends: - R (>= 4.4.0) -RoxygenNote: 7.3.2 -URL: https://github.com/you/MyBiocPackage -BugReports: https://github.com/you/MyBiocPackage/issues diff --git a/templates/NEWS.md b/templates/NEWS.md deleted file mode 100644 index 9340e72..0000000 --- a/templates/NEWS.md +++ /dev/null @@ -1,10 +0,0 @@ -# MyBiocPackage 0.99.0 - -* Initial submission to Bioconductor. - - diff --git a/templates/inst/CITATION b/templates/inst/CITATION deleted file mode 100644 index 1b51d66..0000000 --- a/templates/inst/CITATION +++ /dev/null @@ -1,14 +0,0 @@ -citHeader("To cite MyBiocPackage in publications use:") - -bibentry( - bibtype = "Article", - title = "MyBiocPackage: a short descriptive title", - author = c(person("Given", "Family")), - journal = "Journal Name", - year = "2026", - volume = "0", - number = "0", - pages = "0-0", - doi = "10.0000/000000", - url = "https://github.com/you/MyBiocPackage" -) From ff206b656a9130c46487148bb7cea29c420e78fe Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:24:03 -0700 Subject: [PATCH 06/29] Add scripts/verify.py; split the gate into requirement and recommendation verify.py runs 12 static checks (Python 3 stdlib, no network): manifests, frontmatter, path references that must exist and must not be gitignored, the README tree, Source/Fetched stamps, the SOURCES map against the per-file footers, references to removed tooling, the BiocCheck/biocthis block being identical in all three router files, gate values, emoji, rule text duplicated between AGENTS.md and SKILL.md, and the stamp contract. Run against the pre-fix tree it reports the known defects; clean here except for two files Layer 3 and Layer 5 still have to create. Building the fidelity table surfaced a sixth defect: four of the five gate numbers were stated as requirements when upstream says "should" or "it is recommended that". Only the 5 MB file limit is a "must", and the tracker's own minimum is check plus BiocCheck with no ERROR or WARNING. The gate is now two tiers everywhere it appears, and the review agent reports a tier-2 miss as a warning rather than a blocker. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 29 +- agents/bioc-package-review.md | 22 +- knowledge/development/general-dev.md | 23 +- knowledge/index.md | 20 +- knowledge/workflow.md | 20 +- scripts/verify.py | 593 +++++++++++++++++++++++ skills/bioconductor-package-dev/SKILL.md | 29 +- 7 files changed, 694 insertions(+), 42 deletions(-) create mode 100644 scripts/verify.py diff --git a/AGENTS.md b/AGENTS.md index 68a93a8..66b96c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,14 +22,27 @@ not say "Bioconductor" explicitly but the package clearly targets it. - What reviewers check: `knowledge/reviewer.md` - Appendices (devel Bioc, build options, C/Fortran, etc.): `knowledge/appendices.md` -## Pre-submission gate (hard requirements for a new package) -Do not tell a user their package is submission-ready unless all hold: -- `R CMD check` clean on current R-devel (no errors, no warnings). -- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package' = TRUE)` clean. -- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; - < 8 GB memory for vignettes/examples/tests. -- `Version: 0.99.0`; `biocViews` present; a vignette and man pages present; valid maintainer - email; not on CRAN; hosted on the GitHub default branch. +## Pre-submission gate +Two tiers, because upstream states them at two different strengths. Do not report a tier-2 item +as a blocker; report it as something a reviewer will very likely ask about. + +Tier 1 - stated as requirements: +- `R CMD check` and `BiocCheck` pass with no ERROR and no WARNING on current R-devel. This is the + tracker's own wording: "a minimum requirement for package acceptance". It also says "Passing + these checks does not result in automatic acceptance" - a human review follows. +- Run both entry points: `BiocCheck::BiocCheckGitClone()` and + `BiocCheck::BiocCheck('new-package' = TRUE)`. +- Individual files must be <= 5 MB. Upstream states this one as "must". +- `biocViews` present; a vignette and man pages present; maintainer email valid and belonging to + the person submitting; not on CRAN ("a package can only be submitted to one or the other"); + hosted on the GitHub default branch. BiocCheck catches most of these. + +Tier 2 - stated as should or recommended: +- `Version: 0.99.0` for a new package (upstream: "should set"). Expected in practice; set it. +- Source build under 10 MB (upstream: "should occupy less than"). +- `R CMD check --no-build-vignettes` under 10 min (upstream: "should require less than"). +- Vignettes, examples and tests under 8 GB memory (upstream: "it is recommended that"). + Detail: `knowledge/development/build-check-bioccheck.md` and `knowledge/development/general-dev.md`. ## Version rule diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index 8b93cc6..d546140 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -46,13 +46,25 @@ Data and size (ch 14, 21): Reuse (ch 5): uses standard Bioc classes (SummarizedExperiment, GRanges, etc.) where appropriate rather than reinventing them. -## The gate (report each as pass/fail) -- `R CMD check` clean on R-devel (no errors, no warnings). +## The gate (report each as pass/fail, in two tiers) +Upstream states these at two different strengths, and your report must preserve that. A tier-2 +miss is a warning, never a blocker - calling it a blocker tells the user they cannot submit when +Bioconductor would accept them. + +Tier 1 - requirements: +- `R CMD check` and `BiocCheck` pass with no ERROR and no WARNING on R-devel. The tracker calls + this "a minimum requirement for package acceptance", and adds that passing "does not result in + automatic acceptance". - `BiocCheck::BiocCheckGitClone()` clean. - `BiocCheck::BiocCheck('new-package' = TRUE)` clean. -- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; files <= 5 MB; < 8 GB memory. -- `Version: 0.99.0`; `biocViews`, vignette, man pages present; valid maintainer; not on CRAN; - hosted on GitHub default branch. +- Individual files <= 5 MB (upstream states this as "must"). +- `biocViews`, vignette and man pages present; maintainer email valid and equal to the submitter; + not on CRAN; hosted on the GitHub default branch. + +Tier 2 - should or recommended: +- `Version: 0.99.0` for a new package. Expected in practice; flag a wrong version prominently, + but as a warning. +- Source build under 10 MB; `R CMD check --no-build-vignettes` under 10 min; under 8 GB memory. ## Tooling (use these, do not reimplement them) This repo ships no validator and no templates on purpose - Bioconductor already maintains both, diff --git a/knowledge/development/general-dev.md b/knowledge/development/general-dev.md index 608cfc3..a4f2705 100644 --- a/knowledge/development/general-dev.md +++ b/knowledge/development/general-dev.md @@ -15,20 +15,27 @@ package development features. - Must pass `BiocCheck::BiocCheck('new-package' = TRUE)` with no errors/warnings. - All ERRORs, WARNINGs, and NOTEs must be addressed or explicitly justified. -## Hard numeric thresholds -- Source package produced by `R CMD build`: **< 10 MB**. -- Individual files (software packages): **<= 5 MB** each. -- `R CMD check --no-build-vignettes` runtime: **< 10 minutes**. -- Memory usage across vignettes, examples, and tests: **< 8 GB**. +## Numeric thresholds (note the modality - only one of these is a "must") +- Individual files (software packages): **<= 5 MB** each. Upstream: "individual files must be + <= 5MB". +- Source package produced by `R CMD build`: **< 10 MB**. Upstream: "should occupy less than + 10 MB on disk". +- `R CMD check --no-build-vignettes` runtime: **< 10 minutes**. Upstream: "should require less + than 10 minutes". +- Memory across vignettes, examples, and tests: **< 8 GB**. Upstream: "it is recommended that + the vignettes, man page examples, and unit tests do not require more than 8 GB of memory". - Use lossy compression (e.g., pngquant) to shrink large images/screenshots. +Treat the three "should" items as strong expectations: the build system enforces them in practice +and a reviewer will ask. But do not tell a submitter they are blocked from submitting by them. + ## File hygiene - Do not include filenames that differ only in case (cross-platform safety). - Exclude unnecessary files: `.DS_Store`, `.project`, `.git`, cache files, logs, `*.Rproj`, `*.so`. - Use `.gitignore` to keep undesirable files out of the repository. -- Keep application-specific tooling (GitHub Actions, devtools config) on separate - branches, not in the submitted package. +- Application-specific tooling (GitHub Actions, devtools config) "should be in a different + branch" than the default one holding package code. A recommendation upstream, not a rule. - R CMD check options are customized by Bioconductor via flags configurable through the `R_CHECK_ENVIRON` environment variable. @@ -56,4 +63,4 @@ package development features. Source: https://contributions.bioconductor.org/general.html and https://contributions.bioconductor.org/important-bioconductor-package-development-features.html -Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). +Fetched 2026-08-14 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/index.md b/knowledge/index.md index 51b303c..8e24104 100644 --- a/knowledge/index.md +++ b/knowledge/index.md @@ -43,13 +43,21 @@ Appendices: Rd links, NEWS, references: `appendices.md` (A-H) ## The gate (memorize) -See `workflow.md` and `development/build-check-bioccheck.md` for detail. A new package must: +See `workflow.md` and `development/build-check-bioccheck.md` for detail, including which items are +requirements and which are recommendations - the distinction matters when telling someone whether +they can submit. + +Requirements: - Pass `R CMD check` clean on current R-devel (no errors, no warnings). -- Pass `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package'=TRUE)` clean. -- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; - < 8 GB memory to run vignettes/examples/tests. -- Set `Version: 0.99.0`; include `biocViews`, a vignette, and man pages; valid maintainer email; +- Pass `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package' = TRUE)` clean. +- Individual files <= 5 MB. +- Include `biocViews`, a vignette, and man pages; valid maintainer email equal to the submitter; not already on CRAN; hosted on the GitHub default branch. +Recommendations (expected in practice, but upstream says should/recommended): +- Set `Version: 0.99.0`. +- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; < 8 GB memory to run + vignettes/examples/tests. + Source: https://contributions.bioconductor.org/index.html -Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). +Fetched 2026-08-14 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/knowledge/workflow.md b/knowledge/workflow.md index a22818e..7a5a8ae 100644 --- a/knowledge/workflow.md +++ b/knowledge/workflow.md @@ -48,15 +48,21 @@ rewrite, so check it early even though it is fixed late. Author against the development chapters, then clear every item below. Detail: `development/build-check-bioccheck.md`, `development/general-dev.md`, `development/metadata-files.md`. -Hard gate (all required): -- `Version: 0.99.0` in DESCRIPTION. See `maintenance.md` (version rule) and metadata-files. -- `biocViews` field present and valid; a vignette; man pages for exported objects. -- Valid maintainer email; maintainer == the person who will submit. +Tier 1 - stated as requirements: - `R CMD check` clean on current R-devel (no errors, no warnings). - `BiocCheck::BiocCheckGitClone()` clean. -- `BiocCheck::BiocCheck('new-package' = TRUE)` clean (no errors, no warnings). -- Source build < 10 MB (`R CMD build`); `R CMD check --no-build-vignettes` < 10 min. -- Every individual file <= 5 MB; running vignettes/examples/tests uses < 8 GB memory. +- `BiocCheck::BiocCheck('new-package' = TRUE)` clean (no errors, no warnings). The tracker calls + passing check and BiocCheck "a minimum requirement for package acceptance", and notes that + passing "does not result in automatic acceptance" - review still follows. +- Every individual file <= 5 MB (upstream: "must be"). +- `biocViews` field present and valid; a vignette; man pages for exported objects. +- Valid maintainer email; maintainer == the person who will submit. + +Tier 2 - stated as should or recommended. Expected in practice and a reviewer will ask, but a +miss here is not a blocker: +- `Version: 0.99.0` in DESCRIPTION. See `maintenance.md` (version rule) and metadata-files. +- Source build under 10 MB (`R CMD build`); `R CMD check --no-build-vignettes` under 10 min. +- Running vignettes/examples/tests uses under 8 GB memory. - Bioc code style in R code: `<-`, 4-space indent, 80-col. See `development/r-code.md`. Use the current devel Bioconductor with the matching R version - see `appendices.md` (Appendix A) diff --git a/scripts/verify.py b/scripts/verify.py new file mode 100644 index 0000000..255e908 --- /dev/null +++ b/scripts/verify.py @@ -0,0 +1,593 @@ +#!/usr/bin/env python3 +"""Verify the internal consistency of this repository. + +Layer 1 (default) is static: no network, no R, no LLM, Python 3 stdlib only. It checks the +things that rot silently in a repo made of prose - dead paths, stamps, duplicated rule text +that has drifted between the files that restate it. + +Layer 2 (--network) checks this repo against its upstreams. See check_network(). + + python3 scripts/verify.py + python3 scripts/verify.py --network + python3 scripts/verify.py --list + +Exit status is 1 if any check fails. Warnings never fail the run. +""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import os +import re +import subprocess +import sys +from dataclasses import dataclass, field + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Files that restate the pre-submission gate. Kept in sync deliberately (an agent needs the gate +# in context nearly every turn, so it is duplicated rather than referenced), which is exactly why +# it has to be checked. +GATE_FILES = [ + "AGENTS.md", + "skills/bioconductor-package-dev/SKILL.md", + "agents/bioc-package-review.md", + "knowledge/workflow.md", +] + +# The three router files that carry the identical BiocCheck/biocthis tooling block. +ROUTER_FILES = [ + "AGENTS.md", + "skills/bioconductor-package-dev/SKILL.md", + "agents/bioc-package-review.md", +] + +# Sections that must be byte-identical between AGENTS.md and SKILL.md. +SHARED_SECTIONS = [ + "## Pre-submission gate", + "## Version rule", + "## Bioconductor code style (differs from tidyverse)", +] + +# The five hard numbers of the gate. Every file restating the gate must carry all of them. +GATE_VALUES = ["0.99.0", "10 MB", "10 min", "5 MB", "8 GB"] + +# Tools this repo used to reimplement and must never reference again. +DEAD_REFERENCES = ["check-submission.R", "context/REFRESH.md"] + +# Top-level names that mean "a path inside this repository". Deleted and not-yet-created +# directories are listed on purpose: a reference to one must fail, not be silently skipped. +REPO_PREFIXES = { + ".claude-plugin", ".github", "agents", "context", "docs", "evals", + "knowledge", "scripts", "skills", "templates", +} + +# Basenames that name a file in the *user's* package, not in this repo. +FOREIGN_BASENAMES = {"NEWS.md", "README.md", "CITATION.md", "INSTALL.md"} + +EMOJI = re.compile( + "[\U0001F000-\U0001FAFF☀-➿⬀-⯿️←-⇿⬅-⬇]" +) + +SLUG_URL = re.compile(r"https://contributions\.bioconductor\.org/([A-Za-z0-9._-]+)\.html") + + +@dataclass +class Result: + failures: list[str] = field(default_factory=list) + warnings: list[str] = field(default_factory=list) + + def fail(self, msg: str) -> None: + self.failures.append(msg) + + def warn(self, msg: str) -> None: + self.warnings.append(msg) + + +def git(*args: str) -> str: + return subprocess.run( + ["git", *args], cwd=ROOT, capture_output=True, text=True, check=False + ).stdout + + +def tracked_files() -> list[str]: + return [p for p in git("ls-files").splitlines() if p] + + +def read(path: str) -> str: + with open(os.path.join(ROOT, path), encoding="utf-8") as handle: + return handle.read() + + +def shipped_markdown() -> list[str]: + """Every tracked markdown file. context/ is gitignored, so it is absent by construction.""" + return [p for p in tracked_files() if p.endswith(".md")] + + +def knowledge_files() -> list[str]: + return [ + p for p in tracked_files() + if p.startswith("knowledge/") and p.endswith(".md") and p != "knowledge/SOURCES.md" + ] + + +def section(text: str, heading: str) -> str | None: + """Return the body of a `## heading` section, up to the next heading of the same level.""" + lines = text.splitlines() + try: + start = lines.index(heading) + except ValueError: + return None + body = [] + for line in lines[start + 1:]: + if line.startswith("## "): + break + body.append(line) + return "\n".join(body).strip("\n") + + +# -------------------------------------------------------------------------------------------- +# Check 1 - plugin manifests +# -------------------------------------------------------------------------------------------- + +def check_manifests(res: Result) -> None: + try: + plugin = json.loads(read(".claude-plugin/plugin.json")) + market = json.loads(read(".claude-plugin/marketplace.json")) + except (OSError, json.JSONDecodeError) as exc: + res.fail(f"manifests: cannot parse - {exc}") + return + + for key in ("name", "description", "version"): + if not plugin.get(key): + res.fail(f".claude-plugin/plugin.json: missing required key {key!r}") + for key in ("name", "description", "owner", "plugins"): + if not market.get(key): + res.fail(f".claude-plugin/marketplace.json: missing required key {key!r}") + + name = plugin.get("name") + if name and not os.path.isdir(os.path.join(ROOT, "skills", name)): + res.fail(f"plugin.json name {name!r} has no matching skills/{name}/ directory") + + listed = [p.get("name") for p in market.get("plugins", [])] + if name and name not in listed: + res.fail(f"plugin.json name {name!r} is not listed in marketplace.json plugins {listed}") + + version = plugin.get("version", "") + if not re.fullmatch(r"\d+\.\d+\.\d+", version): + res.fail(f"plugin.json version {version!r} is not x.y.z - users only update on a bump") + + +# -------------------------------------------------------------------------------------------- +# Check 2 - skill and agent frontmatter +# -------------------------------------------------------------------------------------------- + +def frontmatter(path: str) -> dict[str, str]: + text = read(path) + if not text.startswith("---\n"): + return {} + end = text.find("\n---\n", 4) + if end == -1: + return {} + block = text[4:end] + out: dict[str, str] = {} + key = None + for line in block.splitlines(): + m = re.match(r"^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$", line) + if m: + key = m.group(1) + out[key] = m.group(2).strip() + elif key and line.strip(): + out[key] = (out[key] + " " + line.strip()).strip() + return out + + +def check_frontmatter(res: Result) -> None: + skill = "skills/bioconductor-package-dev/SKILL.md" + meta = frontmatter(skill) + if not meta: + res.fail(f"{skill}: no YAML frontmatter") + else: + if meta.get("name") != "bioconductor-package-dev": + res.fail(f"{skill}: frontmatter name {meta.get('name')!r} != directory name") + desc = meta.get("description", "") + if len(desc) < 80: + res.fail(f"{skill}: description is {len(desc)} chars - too thin to route on") + # The target user converts an existing package. If the description stops covering that + # vocabulary the skill silently stops firing for them while every other check passes. + for term in ("existing", "CRAN", "submission"): + if term.lower() not in desc.lower(): + res.fail(f"{skill}: description never mentions {term!r} (conversion audience)") + + agent = "agents/bioc-package-review.md" + meta = frontmatter(agent) + if not meta: + res.fail(f"{agent}: no YAML frontmatter") + return + for key in ("name", "description", "model", "tools"): + if not meta.get(key): + res.fail(f"{agent}: frontmatter missing {key!r}") + expected = os.path.basename(agent)[:-3] + if meta.get("name") != expected: + res.fail(f"{agent}: frontmatter name {meta.get('name')!r} != filename {expected!r}") + if "Write" in meta.get("tools", "") or "Edit" in meta.get("tools", ""): + res.fail(f"{agent}: grants write tools, but the agent is specified as read-only") + + +# -------------------------------------------------------------------------------------------- +# Check 3 - path references resolve and are not gitignored +# -------------------------------------------------------------------------------------------- + +BACKTICKED = re.compile(r"`([^`\n]+)`") + + +def path_candidates(path: str, line: str) -> list[str]: + """Backticked tokens on one line that are meant to name a file in *this* repository. + + Most backticked paths in these documents belong to the user's package (`tests/`, `man/`, + `inst/CITATION`) or to an upstream project, so the filter has to be narrow: a token counts + only if it is rooted at one of this repo's top-level directories, or if it is a relative + markdown reference inside knowledge/. Lines carrying a URL are skipped entirely - they are + naming somebody else's file. + """ + if "http" in line: + return [] + out = [] + for token in BACKTICKED.findall(line): + token = token.strip().replace("${CLAUDE_PLUGIN_ROOT}/", "").rstrip(".,;:") + if not token or " " in token or "(" in token or token.startswith(("<", "-")): + continue + if os.path.basename(token) in FOREIGN_BASENAMES: + continue + rooted = token.split("/", 1)[0] in REPO_PREFIXES + knowledge_ref = path.startswith("knowledge/") and token.endswith(".md") + if rooted or knowledge_ref: + out.append(token) + return out + + +def resolve(path: str, token: str, by_basename: dict[str, list[str]]) -> str | None: + for base in (ROOT, os.path.join(ROOT, os.path.dirname(path)), os.path.join(ROOT, "knowledge")): + if os.path.exists(os.path.join(base, token)): + return os.path.relpath(os.path.join(base, token), ROOT) + if "/" not in token: + hits = by_basename.get(token, []) + if len(hits) == 1: + return hits[0] + return None + + +def check_paths(res: Result) -> None: + by_basename: dict[str, list[str]] = {} + for tracked in tracked_files(): + by_basename.setdefault(os.path.basename(tracked), []).append(tracked) + + ignored: dict[str, bool] = {} + for path in shipped_markdown(): + for lineno, line in enumerate(read(path).splitlines(), 1): + for token in path_candidates(path, line): + resolved = resolve(path, token, by_basename) + if resolved is None: + res.fail(f"{path}:{lineno}: references `{token}`, which does not exist") + continue + if resolved not in ignored: + code = subprocess.run( + ["git", "check-ignore", "-q", resolved], + cwd=ROOT, capture_output=True, check=False, + ).returncode + ignored[resolved] = code == 0 + if ignored[resolved]: + res.fail( + f"{path}:{lineno}: references `{token}`, which is gitignored - " + "dead path in every clone and in the installed plugin" + ) + + +# -------------------------------------------------------------------------------------------- +# Check 4 - README layout tree matches the repository +# -------------------------------------------------------------------------------------------- + +def check_readme_tree(res: Result) -> None: + text = read("README.md") + body = section(text, "## Repository layout") + if body is None: + res.fail("README.md: no '## Repository layout' section") + return + block = re.search(r"```\n(.*?)```", body, re.S) + if not block: + res.fail("README.md: layout section has no fenced tree") + return + + # Only depth-0 entries are compared: a top-level directory disappearing is the failure that + # matters, and mirroring every leaf would make the tree a second source of truth. + listed = set() + for line in block.group(1).splitlines(): + m = re.match(r"^(?:├── |└── )(\S+)", line) + if not m: + continue + listed.add(m.group(1).rstrip("/").split("/")[0]) + + actual = {p.split("/")[0] for p in tracked_files()} + actual.discard(".gitignore") + missing = sorted(actual - listed) + if missing: + res.fail(f"README.md layout tree omits tracked top-level entries: {missing}") + + for entry in sorted(listed - actual): + if entry.startswith("."): + continue + res.warn(f"README.md layout tree lists {entry!r}, which is not tracked yet") + + +# -------------------------------------------------------------------------------------------- +# Check 5 - Source and Fetched stamps +# -------------------------------------------------------------------------------------------- + +STAMP = re.compile(r"^Fetched (\d{4}-\d{2}-\d{2})", re.M) + + +def check_stamps(res: Result) -> None: + today = dt.date.today() + for path in knowledge_files(): + text = read(path) + if not SLUG_URL.search(text): + res.fail(f"{path}: no canonical contributions.bioconductor.org Source: URL") + m = STAMP.search(text) + if not m: + res.fail(f"{path}: no 'Fetched YYYY-MM-DD' stamp") + continue + age = (today - dt.date.fromisoformat(m.group(1))).days + if age > 90: + res.warn(f"{path}: Fetched stamp is {age} days old") + + +# -------------------------------------------------------------------------------------------- +# Check 6 - SOURCES.md map and the per-file Source: footers agree +# -------------------------------------------------------------------------------------------- + +def sources_rows() -> list[tuple[str, str, str]]: + """(rmd, slug, target) rows of the chapter map in knowledge/SOURCES.md.""" + rows = [] + for line in read("knowledge/SOURCES.md").splitlines(): + if not line.startswith("| `") or line.startswith("| ---"): + continue + cells = [c.strip() for c in line.strip("|").split("|")] + if len(cells) != 4: + continue + rmd, slug, _chapter, target = cells + rows.append((rmd.strip("`"), slug.strip("`"), target)) + return rows + + +def check_sources_map(res: Result) -> None: + rows = sources_rows() + if not rows: + res.fail("knowledge/SOURCES.md: chapter map has no parseable rows") + return + + mapped_slugs = {slug for _rmd, slug, _t in rows if slug != "-"} + + # Forward: every mapped slug whose target is a real file must be cited by that file. + for _rmd, slug, target in rows: + if slug == "-" or not target.startswith("`"): + continue + target_path = os.path.join("knowledge", target.strip("`")) + if not os.path.exists(os.path.join(ROOT, target_path)): + res.fail(f"knowledge/SOURCES.md: maps {slug} to {target_path}, which does not exist") + continue + if slug not in SLUG_URL.findall(read(target_path)): + res.fail( + f"knowledge/SOURCES.md: maps slug {slug!r} to {target_path}, " + f"but that file never cites {slug}.html in its Source: footer" + ) + + # Reverse: every slug cited anywhere must be in the map. + for path in knowledge_files(): + for slug in set(SLUG_URL.findall(read(path))): + if slug not in mapped_slugs: + res.fail(f"{path}: cites slug {slug!r}, which is absent from the SOURCES.md map") + + +# -------------------------------------------------------------------------------------------- +# Check 7 - references to tooling this repo deliberately removed +# -------------------------------------------------------------------------------------------- + +def check_dead_references(res: Result) -> None: + for path in shipped_markdown(): + text = read(path) + for needle in DEAD_REFERENCES: + if needle in text: + res.fail(f"{path}: references {needle!r}, which was removed from this repo") + if re.search(r"`templates/", text): + res.fail( + f"{path}: references `templates/`, which was removed - " + "point at biocthis instead" + ) + + +# -------------------------------------------------------------------------------------------- +# Check 8 - the BiocCheck/biocthis block is identical everywhere it appears +# -------------------------------------------------------------------------------------------- + +TOOLING_HEADING = "## Tooling (use these, do not reimplement them)" + + +def tooling_block(text: str) -> str | None: + for m in re.finditer(r"```r\n(.*?)```", text, re.S): + if "BiocCheckGitClone" in m.group(1): + return m.group(1) + return None + + +def check_tooling_block(res: Result) -> None: + blocks = {} + for path in ROUTER_FILES: + text = read(path) + if TOOLING_HEADING not in text: + res.fail(f"{path}: missing the {TOOLING_HEADING!r} section") + block = tooling_block(text) + if block is None: + res.fail(f"{path}: has no BiocCheck/biocthis command block") + continue + blocks[path] = block + + if len(set(blocks.values())) > 1: + res.fail( + "the BiocCheck/biocthis command block differs between " + + ", ".join(sorted(blocks)) + + " - all three must be identical, and golden-path.R runs it" + ) + + for path, block in blocks.items(): + if "BiocCheckGitClone()" not in block or "'new-package' = TRUE" not in block: + res.fail(f"{path}: tooling block does not call both BiocCheck entry points") + if "use_bioc_description" not in block: + res.fail(f"{path}: tooling block does not scaffold with biocthis") + + golden = os.path.join(ROOT, "scripts", "golden-path.R") + if blocks and os.path.exists(golden): + with open(golden, encoding="utf-8") as handle: + script = handle.read() + for call in sorted(set(re.findall(r"use_bioc_\w+", next(iter(blocks.values()))))): + if call not in script: + res.fail( + f"scripts/golden-path.R never calls {call}(), " + "so the documented scaffolding chain is not the tested one" + ) + + +# -------------------------------------------------------------------------------------------- +# Check 9 - the gate numbers appear everywhere the gate is restated +# -------------------------------------------------------------------------------------------- + +def check_gate_values(res: Result) -> None: + for path in GATE_FILES: + text = read(path) + for value in GATE_VALUES: + if value not in text: + res.fail(f"{path}: restates the gate but never mentions {value!r}") + + +# -------------------------------------------------------------------------------------------- +# Check 10 - no emoji +# -------------------------------------------------------------------------------------------- + +def check_no_emoji(res: Result) -> None: + for path in tracked_files(): + if not path.endswith((".md", ".py", ".R", ".json", ".yml", ".yaml")): + continue + for lineno, line in enumerate(read(path).splitlines(), 1): + m = EMOJI.search(line) + if m: + res.fail(f"{path}:{lineno}: emoji {m.group(0)!r} - repo docs are plain text") + + +# -------------------------------------------------------------------------------------------- +# Check 11 - duplicated rule text has not drifted, and the router lives in one place +# -------------------------------------------------------------------------------------------- + +def check_single_source(res: Result) -> None: + agents = read("AGENTS.md") + skill = read("skills/bioconductor-package-dev/SKILL.md") + + for heading in SHARED_SECTIONS: + a, s = section(agents, heading), section(skill, heading) + if a is None: + res.fail(f"AGENTS.md: missing shared section {heading!r}") + if s is None: + res.fail(f"SKILL.md: missing shared section {heading!r}") + if a is not None and s is not None and a != s: + res.fail( + f"section {heading!r} differs between AGENTS.md and SKILL.md - " + "these are duplicated deliberately and must stay byte-identical" + ) + + # The router is AGENTS.md's job. A parallel copy in SKILL.md is what drifts. + for lineno, line in enumerate(skill.splitlines(), 1): + if line.startswith("- ") and "knowledge/" in line: + res.fail( + f"SKILL.md:{lineno}: router-style bullet pointing into knowledge/ - " + "the router belongs in AGENTS.md only" + ) + + +# -------------------------------------------------------------------------------------------- +# Check 12 - the Fetched stamp is a contract, not a decoration +# -------------------------------------------------------------------------------------------- + +def check_stamp_contract(res: Result) -> None: + for path in knowledge_files(): + m = STAMP.search(read(path)) + if not m: + continue # already reported by check 5 + committed = git("log", "-1", "--format=%cs", "--", path).strip() + if not committed: + continue # never committed; nothing to compare against + if dt.date.fromisoformat(committed) > dt.date.fromisoformat(m.group(1)): + res.fail( + f"{path}: last commit {committed} is newer than its Fetched stamp {m.group(1)} - " + "content was edited without restamping, so the provenance is a claim it cannot back" + ) + + +STATIC_CHECKS = [ + ("manifests", check_manifests), + ("frontmatter", check_frontmatter), + ("paths", check_paths), + ("readme-tree", check_readme_tree), + ("stamps", check_stamps), + ("sources-map", check_sources_map), + ("dead-references", check_dead_references), + ("tooling-block", check_tooling_block), + ("gate-values", check_gate_values), + ("no-emoji", check_no_emoji), + ("single-source", check_single_source), + ("stamp-contract", check_stamp_contract), +] + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--network", action="store_true", help="also run upstream fidelity checks") + parser.add_argument("--list", action="store_true", help="list checks and exit") + parser.add_argument("--json", action="store_true", help="emit machine-readable results") + args = parser.parse_args(argv) + + checks = list(STATIC_CHECKS) + if args.network: + from verify_network import NETWORK_CHECKS # noqa: PLC0415 (optional, needs network) + checks += NETWORK_CHECKS + + if args.list: + for name, fn in checks: + print(f"{name:18s} {(fn.__doc__ or '').strip().splitlines()[0] if fn.__doc__ else ''}") + return 0 + + res = Result() + for name, fn in checks: + before = len(res.failures) + try: + fn(res) + except Exception as exc: # a checker crash is a failure, not a pass + res.fail(f"{name}: checker raised {type(exc).__name__}: {exc}") + status = "FAIL" if len(res.failures) > before else "ok" + if not args.json: + print(f"[{status:4s}] {name}") + + if args.json: + print(json.dumps({"failures": res.failures, "warnings": res.warnings}, indent=2)) + else: + for msg in res.warnings: + print(f" warn: {msg}") + for msg in res.failures: + print(f" FAIL: {msg}") + print(f"\n{len(res.failures)} failure(s), {len(res.warnings)} warning(s)") + + return 1 if res.failures else 0 + + +if __name__ == "__main__": + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.exit(main()) diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index b57c416..03875f5 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -29,14 +29,27 @@ from scratch unless there is no package yet. 4. For an end-to-end submission, follow `${CLAUDE_PLUGIN_ROOT}/knowledge/workflow.md`. For an existing package, start at its "Converting an existing package" section. -## Pre-submission gate (hard requirements for a new package) -Do not tell a user their package is submission-ready unless all hold: -- `R CMD check` clean on current R-devel (no errors, no warnings). -- `BiocCheck::BiocCheckGitClone()` and `BiocCheck::BiocCheck('new-package' = TRUE)` clean. -- Source build < 10 MB; `R CMD check --no-build-vignettes` < 10 min; individual files <= 5 MB; - < 8 GB memory for vignettes/examples/tests. -- `Version: 0.99.0`; `biocViews` present; a vignette and man pages present; valid maintainer - email; not on CRAN; hosted on the GitHub default branch. +## Pre-submission gate +Two tiers, because upstream states them at two different strengths. Do not report a tier-2 item +as a blocker; report it as something a reviewer will very likely ask about. + +Tier 1 - stated as requirements: +- `R CMD check` and `BiocCheck` pass with no ERROR and no WARNING on current R-devel. This is the + tracker's own wording: "a minimum requirement for package acceptance". It also says "Passing + these checks does not result in automatic acceptance" - a human review follows. +- Run both entry points: `BiocCheck::BiocCheckGitClone()` and + `BiocCheck::BiocCheck('new-package' = TRUE)`. +- Individual files must be <= 5 MB. Upstream states this one as "must". +- `biocViews` present; a vignette and man pages present; maintainer email valid and belonging to + the person submitting; not on CRAN ("a package can only be submitted to one or the other"); + hosted on the GitHub default branch. BiocCheck catches most of these. + +Tier 2 - stated as should or recommended: +- `Version: 0.99.0` for a new package (upstream: "should set"). Expected in practice; set it. +- Source build under 10 MB (upstream: "should occupy less than"). +- `R CMD check --no-build-vignettes` under 10 min (upstream: "should require less than"). +- Vignettes, examples and tests under 8 GB memory (upstream: "it is recommended that"). + Detail: `knowledge/development/build-check-bioccheck.md` and `knowledge/development/general-dev.md`. ## Version rule From aa64365128dffb27c68f0d6cb95f7b1f061ba5ab Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:28:02 -0700 Subject: [PATCH 07/29] Add the upstream fidelity layer (verify.py --network) Six checks against the five tracked upstreams: pkgrevdocs commit drift mapped through the .Rmd column to the affected summaries, chapter URL liveness, the quotation table, the Contributions issue template, chapter coverage against the guide TOC, and tool version pins. The quotation table is how modality is kept honest. Where a knowledge file states a load-bearing rule it must quote upstream verbatim, and the check asserts the quote is present both upstream and here. Upstream rewording fails the check; paraphrasing a "should" into a "must" fails it too, because the paraphrase no longer matches. Nine rows seeded: the four size and timing numbers, the version rule, the default-branch rule, the CRAN exclusivity rule, and the eligibility list. urllib first, curl second: a TLS-intercepting proxy makes urllib reject certificates curl accepts, and that must not read as upstream drift. Co-Authored-By: Claude Opus 5 --- knowledge/01-submissions.md | 10 +- knowledge/development/general-dev.md | 2 +- knowledge/maintenance.md | 7 +- scripts/__pycache__/verify.cpython-314.pyc | Bin 0 -> 34405 bytes .../verify_network.cpython-314.pyc | Bin 0 -> 24266 bytes scripts/verify.py | 9 +- scripts/verify_network.py | 411 ++++++++++++++++++ 7 files changed, 430 insertions(+), 9 deletions(-) create mode 100644 scripts/__pycache__/verify.cpython-314.pyc create mode 100644 scripts/__pycache__/verify_network.cpython-314.pyc create mode 100644 scripts/verify_network.py diff --git a/knowledge/01-submissions.md b/knowledge/01-submissions.md index 47af4a0..deaffa4 100644 --- a/knowledge/01-submissions.md +++ b/knowledge/01-submissions.md @@ -41,11 +41,11 @@ this is a good thing to raise in the submission issue rather than discover durin - Host the package in a GitHub repository. - The package must live on the repository's DEFAULT branch - you cannot specify an - alternative branch - and that branch "must contain only package code". Files or - directories for other applications (GitHub Actions, devtools, etc) "should be in - a different branch". Upstream states the first as a requirement and the second as - a recommendation; do not report a package as non-compliant solely for carrying a - CI workflow on the default branch. + alternative branch. Upstream: "The default branch must contain only package code. + Any files or directories for other applications (Github Actions, devtools, etc) + should be in a different branch." Note the two different strengths in those two + sentences: do not report a package as non-compliant solely for carrying a CI + workflow on the default branch. - Package name should not conflict (case-insensitive) with any current or past Bioconductor or CRAN package. The contributor grants Bioconductor rights to the package name (CRAN-style naming/ownership policy applies). diff --git a/knowledge/development/general-dev.md b/knowledge/development/general-dev.md index a4f2705..2a33e20 100644 --- a/knowledge/development/general-dev.md +++ b/knowledge/development/general-dev.md @@ -21,7 +21,7 @@ package development features. - Source package produced by `R CMD build`: **< 10 MB**. Upstream: "should occupy less than 10 MB on disk". - `R CMD check --no-build-vignettes` runtime: **< 10 minutes**. Upstream: "should require less - than 10 minutes". + than 10 minutes to run R CMD check --no-build-vignettes". - Memory across vignettes, examples, and tests: **< 8 GB**. Upstream: "it is recommended that the vignettes, man page examples, and unit tests do not require more than 8 GB of memory". - Use lossy compression (e.g., pngquant) to shrink large images/screenshots. diff --git a/knowledge/maintenance.md b/knowledge/maintenance.md index e6b2d72..4e28d35 100644 --- a/knowledge/maintenance.md +++ b/knowledge/maintenance.md @@ -48,7 +48,10 @@ for any change to propagate; broken packages are not published to users. ## Chapter 25 - Version Numbering Format is `x.y.z`: -- New (unaccepted) packages start at `0.99.0` in DESCRIPTION. +- New (unaccepted) packages start at `0.99.0` in DESCRIPTION. Upstream: "New packages submitted + to Bioconductor should set Version: 0.99.0 in the DESCRIPTION file." Stated as a "should", but + it is what reviewers and the build system expect - set it, and treat a wrong version as a + warning rather than as a reason someone cannot submit. - `y` (middle): must be ODD in devel, EVEN in release. Maximum value is 99. - `z` (patch): increment by 1 for EACH git commit in the devel branch. - `x` (major): only ever changed by the Bioconductor team. @@ -184,4 +187,4 @@ git remote prune origin # execute ``` Source: https://contributions.bioconductor.org/git-version-control.html and https://contributions.bioconductor.org/versionnum.html and https://contributions.bioconductor.org/troubleshooting-build-report.html and https://contributions.bioconductor.org/debugging-cc-code.html and https://contributions.bioconductor.org/deprecation.html and https://contributions.bioconductor.org/package-end-of-life-policy.html and https://contributions.bioconductor.org/branch-rename-faqs.html -Fetched 2026-07-23 from contributions.bioconductor.org (Bioconductor devel guide). +Fetched 2026-08-14 from contributions.bioconductor.org (Bioconductor devel guide). diff --git a/scripts/__pycache__/verify.cpython-314.pyc b/scripts/__pycache__/verify.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d5ba206907f33957e4830702077380b875e86ad GIT binary patch literal 34405 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 literal 0 HcmV?d00001 diff --git a/scripts/__pycache__/verify_network.cpython-314.pyc b/scripts/__pycache__/verify_network.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d6b06f01c9303ef791c687b269e795a12dd08ef GIT binary patch literal 24266 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 YAtzTbz%w(K4^}w$ud{rx(E|7X0T?-!z5oCK literal 0 HcmV?d00001 diff --git a/scripts/verify.py b/scripts/verify.py index 255e908..d0e4681 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -67,8 +67,15 @@ # Basenames that name a file in the *user's* package, not in this repo. FOREIGN_BASENAMES = {"NEWS.md", "README.md", "CITATION.md", "INSTALL.md"} +# Spelled with escapes, not literals: a checker that trips its own check is not a good look, and +# box-drawing characters (U+2500-257F, used by the README tree) must stay outside these ranges. EMOJI = re.compile( - "[\U0001F000-\U0001FAFF☀-➿⬀-⯿️←-⇿⬅-⬇]" + "[" + "\U0001f000-\U0001faff" # pictographs, emoticons, transport, symbols + "\u2600-\u27bf" # miscellaneous symbols and dingbats + "\u2b00-\u2bff" # arrows and geometric shapes + "\ufe0f" # variation selector 16 + "]" ) SLUG_URL = re.compile(r"https://contributions\.bioconductor\.org/([A-Za-z0-9._-]+)\.html") diff --git a/scripts/verify_network.py b/scripts/verify_network.py new file mode 100644 index 0000000..520a9b2 --- /dev/null +++ b/scripts/verify_network.py @@ -0,0 +1,411 @@ +#!/usr/bin/env python3 +"""Layer 2: check this repository against the upstreams it summarizes. + +Imported by verify.py when run with --network; not meant to be run on its own. Every check here +needs outbound HTTPS and nothing else - no R, no LLM, no API token. In CI these run on a weekly +cron rather than as a pull-request gate, because upstream changing is a reason to open an issue, +not a reason to block somebody's pull request. + +The fidelity checks work by quotation. Where a knowledge file states a load-bearing rule, it +quotes upstream's own words for that rule, and the check asserts the quote is still present in +both places. That catches the two failures a link checker cannot see: upstream changing a rule, +and a summary restating a recommendation as a requirement. +""" + +from __future__ import annotations + +import html +import json +import os +import re +import ssl +import subprocess +import urllib.error +import urllib.request + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +GUIDE = "https://contributions.bioconductor.org" +USER_AGENT = "bioc_package_dev-verify/1.0 (+https://github.com/ybaeus/bioc_package_dev)" + +# A machine sitting behind a TLS-intercepting proxy has a trust store that urllib rejects and +# curl accepts, which would otherwise make every check here fail for a reason that has nothing +# to do with this repository. So: urllib first, curl second, and never -k in either. +_CURL_ENV = {k: v for k, v in os.environ.items() if k not in ("CURL_CA_BUNDLE", "SSL_CERT_FILE")} + +_cache: dict[str, str] = {} + + +def _curl(url: str, timeout: int, head: bool = False) -> tuple[int, str]: + args = ["curl", "-sS", "--max-time", str(timeout), "-A", USER_AGENT] + args += ["-o", os.devnull, "-w", "%{http_code}", "-I"] if head else ["-w", "\n%{http_code}"] + proc = subprocess.run( + [*args, url], capture_output=True, text=True, env=_CURL_ENV, check=False + ) + if proc.returncode != 0: + return 0, "" + if head: + return int(proc.stdout.strip() or 0), "" + body, _, code = proc.stdout.rpartition("\n") + return int(code.strip() or 0), body + + +def fetch(url: str, timeout: int = 30) -> str: + if url in _cache: + return _cache[url] + req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT}) + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: # noqa: S310 - https only + body = resp.read().decode("utf-8", errors="replace") + except (ssl.SSLError, urllib.error.URLError) as exc: + code, body = _curl(url, timeout) + if code != 200: + raise OSError(f"{url}: urllib said {exc}; curl said HTTP {code}") from exc + _cache[url] = body + return body + + +def fetch_json(url: str) -> object: + return json.loads(fetch(url)) + + +def status(url: str, timeout: int = 30) -> int: + req = urllib.request.Request(url, method="HEAD", headers={"User-Agent": USER_AGENT}) + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: # noqa: S310 + return resp.status + except urllib.error.HTTPError as exc: + return exc.code + except OSError: + return _curl(url, timeout, head=True)[0] + + +def strip_html(page: str) -> str: + page = re.sub(r"(?s)<(script|style).*?", " ", page) + page = re.sub(r"(?s)<[^>]+>", " ", page) + return html.unescape(page) + + +def normalize(text: str) -> str: + """Fold the differences that are not meaning: markup, quote glyphs, wrapping, case.""" + text = text.replace("**", "").replace("`", "").replace("_", "") + text = text.replace("’", "'").replace("‘", "'") + text = text.replace("“", '"').replace("”", '"') + text = text.replace("–", "-").replace("—", "-").replace(" ", " ") + return re.sub(r"\s+", " ", text).strip().lower() + + +def read(path: str) -> str: + with open(os.path.join(ROOT, path), encoding="utf-8") as handle: + return handle.read() + + +def chapter_text(slug: str) -> str: + return normalize(strip_html(fetch(f"{GUIDE}/{slug}.html"))) + + +# -------------------------------------------------------------------------------------------- +# The quotation table - the heart of the fidelity layer +# -------------------------------------------------------------------------------------------- +# Each row: the upstream chapter, the exact upstream wording, and the file in this repo that is +# required to carry that wording verbatim. Hand-maintained on purpose. Automatic sentence +# alignment between a chapter and its summary is unreliable, and a check nobody trusts is a check +# nobody triages - so this covers only the rules that carry weight. + +CLAIMS: list[dict[str, str]] = [ + { + "slug": "general", + "quote": "individual files must be <= 5MB", + "file": "knowledge/development/general-dev.md", + "note": "the only size limit upstream states as a requirement", + }, + { + "slug": "general", + "quote": "should occupy less than 10 MB on disk", + "file": "knowledge/development/general-dev.md", + "note": "source build size - a recommendation, not a blocker", + }, + { + "slug": "general", + "quote": "should require less than 10 minutes to run R CMD check", + "file": "knowledge/development/general-dev.md", + "note": "check duration - a recommendation, not a blocker", + }, + { + "slug": "general", + "quote": ( + "it is recommended that the vignettes, man page examples, and unit tests do not " + "require more than 8 GB of memory" + ), + "file": "knowledge/development/general-dev.md", + "note": "memory ceiling - explicitly a recommendation", + }, + { + "slug": "versionnum", + "quote": "should set version: 0.99.0 in the description file", + "file": "knowledge/maintenance.md", + "note": "the version every new submission starts at", + }, + { + "slug": "bioconductor-package-submissions", + "quote": "the default branch must contain only package code", + "file": "knowledge/01-submissions.md", + "note": "a requirement", + }, + { + "slug": "bioconductor-package-submissions", + "quote": "should be in a different branch", + "file": "knowledge/01-submissions.md", + "note": "CI helper files - a recommendation, and previously over-hardened here", + }, + { + "slug": "bioconductor-package-submissions", + "quote": "a package can only be submitted to one or the other", + "file": "knowledge/01-submissions.md", + "note": "CRAN and Bioconductor are mutually exclusive", + }, + { + "slug": "bioconductor-package-submissions", + "quote": "to submit a package to bioconductor the package should", + "file": "knowledge/01-submissions.md", + "note": "the eligibility list is stated as should, not must", + }, +] + +# The tracker's issue template, not our prose, is the authority on what submission requires. +TRACKER_TEMPLATE = ( + "https://raw.githubusercontent.com/Bioconductor/Contributions/devel/issue_template.md" +) +TRACKER_QUOTES = [ + ( + "a minimum requirement for package acceptance", + ["AGENTS.md", "skills/bioconductor-package-dev/SKILL.md"], + ), + ( + "does not result in automatic acceptance", + ["AGENTS.md", "skills/bioconductor-package-dev/SKILL.md"], + ), +] + + +# -------------------------------------------------------------------------------------------- +# Pins recorded in knowledge/SOURCES.md +# -------------------------------------------------------------------------------------------- + +def pins() -> dict[str, str]: + out: dict[str, str] = {} + for line in read("knowledge/SOURCES.md").splitlines(): + if not line.startswith("| ["): + continue + cells = [c.strip() for c in line.strip("|").split("|")] + if len(cells) < 2: + continue + name, pin = cells[0], cells[1] + sha = re.search(r"`([0-9a-f]{40})`", pin) + tag = re.search(r"`(v[\d.]+)`", pin) + rel = re.search(r"release ([\d.]+)", pin) + dev = re.search(r"devel ([\d.]+)", pin) + if "pkgrevdocs" in name and sha: + out["pkgrevdocs"] = sha.group(1) + elif "Contributions" in name and sha: + out["contributions"] = sha.group(1) + elif "BiocCheck" in name: + if rel: + out["bioccheck_release"] = rel.group(1) + if dev: + out["bioccheck_devel"] = dev.group(1) + elif "biocthis" in name: + if rel: + out["biocthis_release"] = rel.group(1) + if dev: + out["biocthis_devel"] = dev.group(1) + elif "bioc-actions" in name and tag: + out["bioc_actions"] = tag.group(1) + return out + + +def mapped_slugs() -> dict[str, str]: + """slug -> knowledge file (or a 'not summarized' note), from the SOURCES.md chapter map.""" + out = {} + for line in read("knowledge/SOURCES.md").splitlines(): + if not line.startswith("| `"): + continue + cells = [c.strip() for c in line.strip("|").split("|")] + if len(cells) != 4: + continue + _rmd, slug, _ch, target = cells + slug = slug.strip("`") + if slug != "-": + out[slug] = target + return out + + +def rmd_to_slugs() -> dict[str, list[str]]: + out: dict[str, list[str]] = {} + for line in read("knowledge/SOURCES.md").splitlines(): + if not line.startswith("| `"): + continue + cells = [c.strip() for c in line.strip("|").split("|")] + if len(cells) != 4: + continue + rmd, slug, _ch, target = cells + out.setdefault(rmd.strip("`"), []).append(f"{slug.strip('`')} -> {target}") + return out + + +# -------------------------------------------------------------------------------------------- +# Checks +# -------------------------------------------------------------------------------------------- + +def check_commit_drift(res) -> None: + """pkgrevdocs has moved: report which summaries the changed chapters map to.""" + pinned = pins().get("pkgrevdocs") + if not pinned: + res.fail("knowledge/SOURCES.md: no pkgrevdocs commit pin found") + return + head = fetch_json("https://api.github.com/repos/Bioconductor/pkgrevdocs/commits/devel") + current = head["sha"] # type: ignore[index] + if current == pinned: + return + + compare = fetch_json( + f"https://api.github.com/repos/Bioconductor/pkgrevdocs/compare/{pinned}...{current}" + ) + changed = [f["filename"] for f in compare.get("files", [])] # type: ignore[union-attr] + table = rmd_to_slugs() + affected = sorted({t for name in changed for t in table.get(name, [])}) + res.fail( + "pkgrevdocs moved from {} to {}. Changed files: {}. Affected summaries: {}. " + "Compare: https://github.com/Bioconductor/pkgrevdocs/compare/{}...{}".format( + pinned[:7], current[:7], + ", ".join(changed) or "(none reported)", + ", ".join(affected) or "(none mapped - check for a new chapter)", + pinned, current, + ) + ) + + +def check_url_liveness(res) -> None: + """Every canonical chapter URL cited by a summary still resolves.""" + cited = set() + for dirpath, _dirs, names in os.walk(os.path.join(ROOT, "knowledge")): + for name in names: + if not name.endswith(".md"): + continue + rel = os.path.relpath(os.path.join(dirpath, name), ROOT) + cited.update(re.findall(rf"{GUIDE}/([A-Za-z0-9._-]+)\.html", read(rel))) + for slug in sorted(cited): + code = status(f"{GUIDE}/{slug}.html") + if code != 200: + res.fail(f"{GUIDE}/{slug}.html returned {code} - chapter renamed or removed") + + +def check_quotations(res) -> None: + """Load-bearing rules are quoted verbatim from upstream, in both places.""" + for claim in CLAIMS: + quote = normalize(claim["quote"]) + try: + upstream = chapter_text(claim["slug"]) + except OSError as exc: + res.fail(f"cannot fetch {claim['slug']}.html: {exc}") + continue + if quote not in upstream: + res.fail( + f"upstream {claim['slug']}.html no longer contains {claim['quote']!r} " + f"({claim['note']}) - re-read the chapter before touching {claim['file']}" + ) + continue + if quote not in normalize(read(claim["file"])): + res.fail( + f"{claim['file']} must quote upstream verbatim: {claim['quote']!r} " + f"({claim['note']}). Paraphrasing here is how a recommendation " + "silently becomes a requirement." + ) + + +def check_tracker_gate(res) -> None: + """The submission tracker's checklist, not our prose, defines the gate.""" + pinned = pins().get("contributions") + latest = fetch_json( + "https://api.github.com/repos/Bioconductor/Contributions/commits" + "?path=issue_template.md&per_page=1" + ) + current = latest[0]["sha"] # type: ignore[index] + if pinned and current != pinned: + res.fail( + f"Contributions/issue_template.md moved from {pinned[:7]} to {current[:7]}. " + "This template is the authoritative submission checklist - read the diff in full " + "and update the gate in AGENTS.md, SKILL.md and agents/bioc-package-review.md. " + "https://github.com/Bioconductor/Contributions/commits/devel/issue_template.md" + ) + + template = normalize(fetch(TRACKER_TEMPLATE)) + for quote, files in TRACKER_QUOTES: + if normalize(quote) not in template: + res.fail(f"issue_template.md no longer says {quote!r} - the gate wording is stale") + continue + for path in files: + if normalize(quote) not in normalize(read(path)): + res.fail(f"{path} does not carry the tracker's own wording {quote!r}") + + +def check_chapter_coverage(res) -> None: + """A chapter added upstream is invisible to every per-file check, so diff the whole list.""" + index = fetch(f"{GUIDE}/index.html") + upstream = [] + for m in re.finditer(r'href="([A-Za-z0-9._-]+)\.html"', index): + if m.group(1) not in upstream: + upstream.append(m.group(1)) + known = mapped_slugs() + for slug in upstream: + if slug not in known: + res.fail( + f"upstream chapter {slug}.html is absent from the SOURCES.md map - " + "either summarize it or record it as deliberately not summarized" + ) + for slug in known: + if slug not in upstream and slug != "index": + res.warn(f"SOURCES.md maps {slug}, which no longer appears in the guide TOC") + + +def check_tool_pins(res) -> None: + """BiocCheck, biocthis and bioc-actions releases move independently of the guide.""" + recorded = pins() + views = {} + for channel in ("release", "devel"): + pkg = None + for line in fetch(f"https://bioconductor.org/packages/{channel}/bioc/VIEWS").splitlines(): + if line.startswith("Package:"): + pkg = line.split(":", 1)[1].strip() + elif line.startswith("Version:") and pkg in ("BiocCheck", "biocthis"): + views[f"{pkg.lower()}_{channel}"] = line.split(":", 1)[1].strip() + + for key, label in ( + ("bioccheck_release", "BiocCheck release"), + ("bioccheck_devel", "BiocCheck devel"), + ("biocthis_release", "biocthis release"), + ("biocthis_devel", "biocthis devel"), + ): + current = views.get(key) + if current and recorded.get(key) and current != recorded[key]: + res.warn( + f"{label} is now {current}, pinned at {recorded[key]} in knowledge/SOURCES.md" + ) + + tags = fetch_json("https://api.github.com/repos/grimbough/bioc-actions/tags?per_page=1") + if tags: + newest = tags[0]["name"] # type: ignore[index] + if recorded.get("bioc_actions") and newest != recorded["bioc_actions"]: + res.warn( + f"bioc-actions latest tag is {newest}, pinned at {recorded['bioc_actions']} - " + "read the action.yml diff before bumping .github/workflows/verify.yml" + ) + + +NETWORK_CHECKS = [ + ("commit-drift", check_commit_drift), + ("url-liveness", check_url_liveness), + ("quotations", check_quotations), + ("tracker-gate", check_tracker_gate), + ("chapter-coverage", check_chapter_coverage), + ("tool-pins", check_tool_pins), +] From ccd87ce403f7c0854ca86bea4c6244d37f2fa68b Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:31:08 -0700 Subject: [PATCH 08/29] Add the golden path and wire all layers to CI scripts/golden-path.R runs the biocthis chain from the documented block against a throwaway package, then asserts the chain produced what the gate asks for - DESCRIPTION with biocViews and Version 0.99.0, NAMESPACE, NEWS.md, inst/CITATION, a vignette and a man page. A biocthis rename fails here with a clear message rather than deep inside BiocCheck output. .github/workflows/verify.yml runs the layers on four different triggers: static on every push, golden-path on pull requests and weekly, fidelity weekly only (it opens or updates a single upstream-drift issue instead of gating anyone's pull request), evals on manual dispatch. New static check: the workflow must pin bioc-actions to the tag recorded in SOURCES.md, and no action may be pinned to a branch. Not yet executed anywhere: golden-path.R needs R 4.6 and Bioconductor devel, and this machine has R 4.4 with none of usethis, biocthis or BiocCheck installed. It parses, and it fails cleanly with an install message when a dependency is missing. Its first real run will be in CI. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 154 +++++++++++++++++++++++++++++++++++ scripts/golden-path.R | 141 ++++++++++++++++++++++++++++++++ scripts/verify.py | 32 ++++++++ 3 files changed, 327 insertions(+) create mode 100644 .github/workflows/verify.yml create mode 100644 scripts/golden-path.R diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..80ce67a --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,154 @@ +name: verify + +# Four jobs, deliberately on four different triggers. The static checks are cheap enough to gate +# every push. The golden path costs a Bioconductor install, so it runs on pull requests and on a +# schedule. The fidelity job never gates a pull request: upstream changing is a reason to open an +# issue, not a reason to block somebody's work. Evals cost tokens and run only when asked. + +on: + push: + branches: [main, dev] + pull_request: + schedule: + # Mondays, 06:00 UTC. + - cron: "0 6 * * 1" + workflow_dispatch: + inputs: + run-evals: + description: "Also run the behavioral evals (costs tokens)" + type: boolean + default: false + +permissions: + contents: read + +jobs: + static: + name: static checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # verify.py compares each knowledge file's Fetched stamp against the date of the commit + # that last touched it, which a shallow clone cannot answer. + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python3 scripts/verify.py + + fidelity: + name: upstream fidelity + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Check against upstream + id: check + continue-on-error: true + # `shell: bash` brings -o pipefail, without which tee would mask the failing exit status. + shell: bash + run: python3 scripts/verify.py --network --json | tee fidelity.json + + - name: Open or update the drift issue + if: steps.check.outcome == 'failure' + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + title="Upstream drift detected by the fidelity job" + body=$(mktemp) + { + echo "\`scripts/verify.py --network\` failed on $(date -u +%Y-%m-%d)." + echo + echo "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + echo + echo "Work these top-down - the ordering in \`docs/REFRESH.md\` is by blast radius:" + echo + echo '```' + python3 -c "import json;d=json.load(open('fidelity.json'));[print('-',f) for f in d['failures']];[print('- warn:',w) for w in d['warnings']]" + echo '```' + } > "$body" + + existing=$(gh issue list --state open --label upstream-drift --json number --jq '.[0].number') + if [ -n "$existing" ]; then + gh issue comment "$existing" --body-file "$body" + else + gh label create upstream-drift --color FBCA04 --description "Tracked upstream moved" \ + --force >/dev/null 2>&1 || true + gh issue create --title "$title" --label upstream-drift --body-file "$body" + fi + + - name: Fail the job if upstream drifted + if: steps.check.outcome == 'failure' + run: exit 1 + + golden-path: + name: golden path + if: github.event_name != 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: grimbough/bioc-actions/setup-bioc@v1.0.16 + with: + bioc-version: devel + + - uses: grimbough/bioc-actions/use-bioc-caches@v1.0.16 + + - name: Install the tools this repo recommends + run: | + Rscript -e 'BiocManager::install(c("biocthis", "BiocCheck", "roxygen2"), ask = FALSE)' + + - name: Run the documented scaffolding chain + run: Rscript scripts/golden-path.R "${{ github.workspace }}/GoldenPathPkg" + + - uses: grimbough/bioc-actions/build-install-check@v1.0.16 + with: + package-directory: GoldenPathPkg + + - uses: grimbough/bioc-actions/run-BiocCheck@v1.0.16 + with: + package-directory: GoldenPathPkg + # Start at error. Tighten to warning once the baseline is known green - a scaffolded + # package legitimately trips some BiocCheck notes (no real vignette content yet). + error-on: error + + evals: + name: behavioral evals + if: github.event_name == 'workflow_dispatch' && inputs.run-evals + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Require an API key + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + if [ -z "${ANTHROPIC_API_KEY:-}" ]; then + echo "ANTHROPIC_API_KEY is not set - add it as a repository secret." >&2 + exit 1 + fi + + - uses: actions/setup-node@v4 + with: + node-version: "22" + + - run: npm install -g @anthropic-ai/claude-code + + - name: Validate the plugin manifests + run: claude plugin validate . + + - name: Run the eval suite + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: claude plugin eval . --ablation with-without diff --git a/scripts/golden-path.R b/scripts/golden-path.R new file mode 100644 index 0000000..1784d22 --- /dev/null +++ b/scripts/golden-path.R @@ -0,0 +1,141 @@ +#!/usr/bin/env Rscript + +## Build a throwaway package by running, verbatim, the scaffolding chain this repo tells users +## to run. Nothing here is a template: every file comes from biocthis. CI then puts the result +## through R CMD build, R CMD check and BiocCheck, so a green build means the instructions in +## AGENTS.md, SKILL.md and agents/bioc-package-review.md are instructions that were actually +## tested end to end, not instructions that merely look right. +## +## Usage: +## Rscript scripts/golden-path.R [output-directory] +## +## Default output directory is /tmp/GoldenPathPkg (override with GOLDEN_PATH_DIR). +## scripts/verify.py asserts that every use_bioc_*() call in the documented block appears here. + +options( + usethis.quiet = TRUE, + warn = 1, + repos = c(CRAN = "https://cloud.r-project.org") +) + +args <- commandArgs(trailingOnly = TRUE) +outdir <- if (length(args) >= 1) { + args[[1]] +} else { + Sys.getenv("GOLDEN_PATH_DIR", file.path(tempdir(), "GoldenPathPkg")) +} +pkg <- basename(outdir) + +for (dep in c("usethis", "biocthis", "roxygen2")) { + if (!requireNamespace(dep, quietly = TRUE)) { + stop("golden-path needs ", dep, ": BiocManager::install(\"", dep, "\")", call. = FALSE) + } +} + +say <- function(...) cat("[golden-path]", ..., "\n", sep = " ") + +if (dir.exists(outdir)) { + say("removing previous", outdir) + unlink(outdir, recursive = TRUE) +} +dir.create(dirname(outdir), recursive = TRUE, showWarnings = FALSE) + +say("creating package", pkg, "in", outdir) +usethis::create_package(outdir, open = FALSE, rstudio = FALSE) +usethis::proj_set(outdir, force = TRUE) + +## A package with no code has nothing to document, and BiocCheck has opinions about that. One +## exported function, written in Bioconductor style (assignment arrow, four-space indent, +## seq_along rather than 1:n) so the example is not quietly teaching the wrong habits. +dir.create(file.path(outdir, "R"), showWarnings = FALSE) +writeLines( + c( + "#' Count non-missing values per column", + "#'", + "#' @param x A `data.frame` or matrix.", + "#'", + "#' @return An integer vector, one element per column of `x`.", + "#'", + "#' @examples", + "#' countObserved(data.frame(a = c(1, NA), b = c(2, 3)))", + "#'", + "#' @export", + "countObserved <- function(x) {", + " stopifnot(length(dim(x)) == 2L)", + " counts <- integer(ncol(x))", + " for (i in seq_along(counts)) {", + " counts[[i]] <- sum(!is.na(x[, i]))", + " }", + " names(counts) <- colnames(x)", + " counts", + "}" + ), + file.path(outdir, "R", "countObserved.R") +) + +## --------------------------------------------------------------------------------------- +## The documented chain. Keep these calls identical to the block in AGENTS.md, SKILL.md and +## agents/bioc-package-review.md - verify.py check 8 fails the build if they drift apart. +## --------------------------------------------------------------------------------------- + +say("biocthis::use_bioc_description()") +biocthis::use_bioc_description(biocViews = "Software") + +say("biocthis::use_bioc_news_md()") +biocthis::use_bioc_news_md(open = FALSE) + +say("biocthis::use_bioc_vignette()") +biocthis::use_bioc_vignette(name = pkg, title = paste("Introduction to", pkg)) + +say("biocthis::use_bioc_citation()") +biocthis::use_bioc_citation() + +say("biocthis::use_bioc_github_action()") +biocthis::use_bioc_github_action() + +## --------------------------------------------------------------------------------------- +## The two things the guide requires that biocthis does not decide for you. +## --------------------------------------------------------------------------------------- + +say("setting Version: 0.99.0") +desc_path <- file.path(outdir, "DESCRIPTION") +desc <- readLines(desc_path) +desc[grepl("^Version:", desc)] <- "Version: 0.99.0" +writeLines(desc, desc_path) + +say("roxygenise (man pages for exported objects)") +roxygen2::roxygenise(outdir, load_code = roxygen2::load_source) + +## --------------------------------------------------------------------------------------- +## Assert the chain produced what the gate asks for. A silent rename upstream (a use_bioc_*() +## function that no longer writes NEWS.md, say) has to fail here rather than three steps later +## inside BiocCheck output nobody reads. +## --------------------------------------------------------------------------------------- + +expected <- c( + "DESCRIPTION", + "NAMESPACE", + "NEWS.md", + "inst/CITATION", + file.path("vignettes", paste0(pkg, ".Rmd")), + file.path("man", "countObserved.Rd") +) +missing <- expected[!file.exists(file.path(outdir, expected))] +if (length(missing) > 0L) { + stop( + "the documented scaffolding chain did not produce: ", + paste(missing, collapse = ", "), + call. = FALSE + ) +} + +desc <- readLines(desc_path) +if (!any(grepl("^Version: 0\\.99\\.0$", desc))) { + stop("DESCRIPTION is not at Version: 0.99.0", call. = FALSE) +} +if (!any(grepl("^biocViews:", desc))) { + stop("use_bioc_description() no longer writes a biocViews field", call. = FALSE) +} + +say("ok:", outdir) +cat(outdir, "\n", sep = "") diff --git a/scripts/verify.py b/scripts/verify.py index d0e4681..7e6c749 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -539,6 +539,37 @@ def check_stamp_contract(res: Result) -> None: ) +# -------------------------------------------------------------------------------------------- +# Check 13 - the CI workflow pins the action version recorded in SOURCES.md +# -------------------------------------------------------------------------------------------- + +WORKFLOW = ".github/workflows/verify.yml" + + +def check_workflow_pins(res: Result) -> None: + if not os.path.exists(os.path.join(ROOT, WORKFLOW)): + res.fail(f"{WORKFLOW} does not exist - the verification layers are not wired to CI") + return + text = read(WORKFLOW) + + for m in re.finditer(r"uses:\s*(\S+)@(\S+)", text): + action, ref = m.groups() + if ref in ("main", "master", "devel", "HEAD"): + res.fail(f"{WORKFLOW}: {action} is pinned to a branch ({ref}), not a tag") + + pinned = re.search(r"bioc-actions.*?\|\s*`(v[\d.]+)`", read("knowledge/SOURCES.md")) + if not pinned: + res.fail("knowledge/SOURCES.md: no bioc-actions tag pin found") + return + used = set(re.findall(r"grimbough/bioc-actions/\S+@(\S+)", text)) + wrong = sorted(used - {pinned.group(1)}) + if wrong: + res.fail( + f"{WORKFLOW} uses bioc-actions {wrong}, but knowledge/SOURCES.md pins " + f"{pinned.group(1)} - bump both together or neither" + ) + + STATIC_CHECKS = [ ("manifests", check_manifests), ("frontmatter", check_frontmatter), @@ -552,6 +583,7 @@ def check_stamp_contract(res: Result) -> None: ("no-emoji", check_no_emoji), ("single-source", check_single_source), ("stamp-contract", check_stamp_contract), + ("workflow-pins", check_workflow_pins), ] From 4beb46f39991f95ebf0129947e972ab406dab349 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:38:36 -0700 Subject: [PATCH 09/29] Add the behavioral eval suite Ten cases under evals/, one per prompt documented in the README. Three guard the skill description, which is the single point of failure that can make the whole plugin inert while every static check passes green: conversion, implicit (never says "Bioconductor"), and a negative case that must stay quiet on a plain CRAN question. Two agent cases audit a fixture package with exactly three planted defects - Version 0.1.0, no biocViews, no vignette - built by shell only, so they need neither R nor biocthis. Their LLM graders name both the findings that count and the false positives that do not. New static check: every quoted prompt in the README's Example prompts section must appear verbatim as some case's execution.prompt, so the documented examples cannot drift away from the tested ones. Not yet executed: `claude plugin eval` is early access and unavailable in the local CLI. The cases are written against the schema the CLI validates, but have never been run, and the evals job is dispatch-only so a broken case cannot break anything else. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 10 ++- .gitignore | 1 + README.md | 2 +- evals/README.md | 64 +++++++++++++++ evals/agent-review/case.yaml | 98 +++++++++++++++++++++++ evals/agent-verdict/case.yaml | 95 ++++++++++++++++++++++ evals/anti-trigger/case.yaml | 30 +++++++ evals/facts-version/case.yaml | 23 ++++++ evals/router-large-data/case.yaml | 22 +++++ evals/style-bioc/case.yaml | 26 ++++++ evals/trigger-conversion/case.yaml | 26 ++++++ evals/trigger-cran-move/case.yaml | 23 ++++++ evals/trigger-implicit/case.yaml | 22 +++++ evals/workflow-after-submission/case.yaml | 27 +++++++ scripts/verify.py | 45 +++++++++++ 15 files changed, 512 insertions(+), 2 deletions(-) create mode 100644 evals/README.md create mode 100644 evals/agent-review/case.yaml create mode 100644 evals/agent-verdict/case.yaml create mode 100644 evals/anti-trigger/case.yaml create mode 100644 evals/facts-version/case.yaml create mode 100644 evals/router-large-data/case.yaml create mode 100644 evals/style-bioc/case.yaml create mode 100644 evals/trigger-conversion/case.yaml create mode 100644 evals/trigger-cran-move/case.yaml create mode 100644 evals/trigger-implicit/case.yaml create mode 100644 evals/workflow-after-submission/case.yaml diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 80ce67a..2711a86 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -151,4 +151,12 @@ jobs: - name: Run the eval suite env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: claude plugin eval . --ablation with-without + # --scaffold runs each case's scaffold_script, which builds the broken fixture package + # the two agent cases audit. The cases in this repository were authored here. + run: claude plugin eval . --ablation with-without --scaffold --report eval-report.html + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: eval-report + path: eval-report.html diff --git a/.gitignore b/.gitignore index 4421733..39762ea 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ context/ # OS .DS_Store Thumbs.db +evals/results/ diff --git a/README.md b/README.md index 16392c3..02305ea 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ firing while every file still looks fine. Four layers catch different failures. | Static | `python3 scripts/verify.py` | broken internal paths, gitignored references, manifest and frontmatter errors, missing `Source:`/`Fetched` stamps, rule text that has drifted between the files that duplicate it, emoji, a stale README tree | | Fidelity | `python3 scripts/verify.py --network` | upstream commit drift mapped to the affected summaries, dead chapter URLs, thresholds that no longer match upstream, summaries that harden an upstream "should" into a "must", gate wording that no longer matches the submission tracker, new upstream chapters nobody summarized | | Golden path | `Rscript scripts/golden-path.R` + `R CMD build` + `BiocCheck` in CI | the scaffolding commands this repo tells you to run, by running them and checking the result with real Bioconductor tooling | -| Behavior | `claude plugin eval .` | the skill firing when it should, staying quiet when it should not, and returning the right values - including every prompt in "Example prompts" above | +| Behavior | `claude plugin eval . --scaffold` | the skill firing when it should, staying quiet when it should not, and returning the right values - including every prompt in "Example prompts" above | The fidelity job is a weekly cron rather than a PR gate: upstream changing is a reason to open an issue, not to block someone's pull request. diff --git a/evals/README.md b/evals/README.md new file mode 100644 index 0000000..2ac3e58 --- /dev/null +++ b/evals/README.md @@ -0,0 +1,64 @@ +# Behavioral evals + +Layer 4 of the verification described in the top-level README. The other three layers check what +the files say; these check what the plugin does. + +``` +claude plugin eval . --ablation with-without --scaffold +claude plugin eval . --case trigger-conversion --verbose +``` + +`--scaffold` is required for the two agent cases: it runs each case's `scaffold_script`, which +builds the deliberately broken fixture package they audit. The flag is off by default because a +`scaffold_script` is author-supplied shell; these were authored here. + +`--ablation with-without` adds a no-plugin baseline arm and reports the delta. That delta is the +evidence, not the absolute score - a model can answer "what version for a new submission" from +memory, and a case that passes equally well without the plugin is a case that is not measuring +this plugin. + +## Why these cases + +| Case | What breaks if it fails | +|---|---| +| `trigger-conversion` | the skill no longer fires for the audience it was written for | +| `trigger-cran-move` | someone is told they can be on CRAN and Bioconductor at once | +| `trigger-implicit` | the skill only works when the user already knows the word "Bioconductor" | +| `anti-trigger` | the skill fires on everything, spending context and dragging Bioconductor rules into CRAN answers | +| `facts-version` | the single most asked question gets a plausible wrong answer | +| `router-large-data` | large data advice stops at "compress it" instead of ExperimentHub | +| `style-bioc` | generated code uses tidyverse habits and `1:n`, which BiocCheck flags | +| `workflow-after-submission` | the post-submission sequence is missing or out of order | +| `agent-review` | the review agent misses planted defects, or invents ones that are not there | +| `agent-verdict` | the agent hedges instead of answering, or reports a recommendation as a blocker | + +The trigger cases are the highest-value three. The `description` field in `SKILL.md` is the +single point of failure that can make the whole plugin inert while every static check still +passes, and nothing but a behavioral eval can see it. + +## How the graders are built + +Deterministic regex wherever the claim is checkable that way, which is most of them and costs +nothing. LLM graders only on the two agent cases, where the thing being judged is whether a +report is accurate and correctly scoped - and there the criteria name the specific findings that +count and the specific false positives that do not, because a vague criterion just moves the +judgement somewhere it cannot be inspected. + +The agent cases grade the content of the answer rather than asserting that the subagent was +spawned. Whether the work happens in the main loop or in `bioc-package-review`, the user-visible +requirement is the same: find the real defects, invent none, and do not claim a check result that +was never measured. + +## Keeping this in step with the README + +Every prompt in the top-level README's "Example prompts" section appears verbatim as some case's +`execution.prompt`. `scripts/verify.py` enforces it, so a documented prompt is always one CI has +an opinion about, and editing either side without the other fails the static check. + +## Not yet executed + +`claude plugin eval` is early access and was not available in the CLI on the machine where these +were written, so the cases are authored against the schema the CLI validates against but have +never been run. Expect the first run to need adjustment - most likely to the regex graders, which +assert on phrasing rather than on meaning. The evals job is dispatch-only, so a broken case here +cannot break anything else. diff --git a/evals/agent-review/case.yaml b/evals/agent-review/case.yaml new file mode 100644 index 0000000..601b0a1 --- /dev/null +++ b/evals/agent-review/case.yaml @@ -0,0 +1,98 @@ +schema_version: "1.0" +name: agent-review +description: >- + Audits a package with exactly three planted defects: Version 0.1.0 instead of 0.99.0, no + biocViews field, and no vignette. Everything else is deliberately fine, so a report that finds + the three and does not invent a fourth is the pass condition. The fixture is built with shell + only - no R, no biocthis - so this case runs anywhere and cannot fail for an unrelated reason. +tags: [agent, review] +plugins: [bioconductor-package-dev] +runs: 2 + +context: + scaffold_script: | + set -euo pipefail + rm -rf BrokenPkg + mkdir -p BrokenPkg/R BrokenPkg/man BrokenPkg/tests/testthat + cat > BrokenPkg/DESCRIPTION <<'EOF' + Package: BrokenPkg + Type: Package + Title: Summarise Counts Across Samples + Version: 0.1.0 + Authors@R: person("Ada", "Lovelace", email = "ada@example.org", role = c("aut", "cre")) + Description: Summarises count matrices across samples for downstream analysis. + License: Artistic-2.0 + Encoding: UTF-8 + Imports: SummarizedExperiment + Suggests: testthat + RoxygenNote: 7.3.2 + EOF + cat > BrokenPkg/NAMESPACE <<'EOF' + export(summariseCounts) + importFrom(SummarizedExperiment,assay) + EOF + cat > BrokenPkg/R/summariseCounts.R <<'EOF' + #' Summarise counts across samples + #' + #' @param se A SummarizedExperiment. + #' @return A numeric vector of column sums. + #' @export + summariseCounts <- function(se) { + counts <- SummarizedExperiment::assay(se) + colSums(counts) + } + EOF + cat > BrokenPkg/man/summariseCounts.Rd <<'EOF' + \name{summariseCounts} + \alias{summariseCounts} + \title{Summarise counts across samples} + \usage{summariseCounts(se)} + \arguments{\item{se}{A SummarizedExperiment.}} + \value{A numeric vector of column sums.} + \description{Summarise counts across samples.} + \examples{ + library(SummarizedExperiment) + se <- SummarizedExperiment(assays = list(counts = matrix(1:4, nrow = 2))) + summariseCounts(se) + } + EOF + cat > BrokenPkg/tests/testthat.R <<'EOF' + library(testthat) + library(BrokenPkg) + test_check("BrokenPkg") + EOF + cat > BrokenPkg/tests/testthat/test-summariseCounts.R <<'EOF' + test_that("summariseCounts returns one value per column", { + expect_length(summariseCounts(se), 2L) + }) + EOF + +execution: + prompt: >- + Audit my package for Bioconductor submission readiness. The package is in the BrokenPkg + directory. + max_turns: 25 + timeout_seconds: 900 + allowed_tools: [Read, Grep, Glob] + +graders: + - type: regex + name: finds-the-version + pattern: "0\\.99\\.0" + - type: regex + name: finds-the-missing-biocviews + pattern: "biocViews" + - type: regex + name: finds-the-missing-vignette + pattern: "vignette" + flags: "i" + - type: llm + name: report-is-accurate-and-scoped + criteria: >- + The response reports all three real problems with the package: the Version field is 0.1.0 + rather than 0.99.0, the DESCRIPTION has no biocViews field, and there is no vignette. It + must not claim the package is ready to submit. It must not report problems that do not + exist - the package does have a NAMESPACE with explicit exports, a man page with a runnable + example, and unit tests under tests/, so claiming any of those is missing is a failure. If + it reports the check or BiocCheck gate items, it says they were not run rather than + asserting a result it did not measure. diff --git a/evals/agent-verdict/case.yaml b/evals/agent-verdict/case.yaml new file mode 100644 index 0000000..1c62499 --- /dev/null +++ b/evals/agent-verdict/case.yaml @@ -0,0 +1,95 @@ +schema_version: "1.0" +name: agent-verdict +description: >- + Same fixture as agent-review, different question. This one tests the part that is easy to get + wrong in the pleasant direction: asked whether a package would pass, the answer has to be no, + and it has to preserve the two tiers - a wrong version is a warning, a missing biocViews and a + missing vignette are blockers. Encouraging vagueness here is the failure mode. +tags: [agent, review, verdict] +plugins: [bioconductor-package-dev] +runs: 2 + +context: + scaffold_script: | + set -euo pipefail + rm -rf BrokenPkg + mkdir -p BrokenPkg/R BrokenPkg/man BrokenPkg/tests/testthat + cat > BrokenPkg/DESCRIPTION <<'EOF' + Package: BrokenPkg + Type: Package + Title: Summarise Counts Across Samples + Version: 0.1.0 + Authors@R: person("Ada", "Lovelace", email = "ada@example.org", role = c("aut", "cre")) + Description: Summarises count matrices across samples for downstream analysis. + License: Artistic-2.0 + Encoding: UTF-8 + Imports: SummarizedExperiment + Suggests: testthat + RoxygenNote: 7.3.2 + EOF + cat > BrokenPkg/NAMESPACE <<'EOF' + export(summariseCounts) + importFrom(SummarizedExperiment,assay) + EOF + cat > BrokenPkg/R/summariseCounts.R <<'EOF' + #' Summarise counts across samples + #' + #' @param se A SummarizedExperiment. + #' @return A numeric vector of column sums. + #' @export + summariseCounts <- function(se) { + counts <- SummarizedExperiment::assay(se) + colSums(counts) + } + EOF + cat > BrokenPkg/man/summariseCounts.Rd <<'EOF' + \name{summariseCounts} + \alias{summariseCounts} + \title{Summarise counts across samples} + \usage{summariseCounts(se)} + \arguments{\item{se}{A SummarizedExperiment.}} + \value{A numeric vector of column sums.} + \description{Summarise counts across samples.} + \examples{ + library(SummarizedExperiment) + se <- SummarizedExperiment(assays = list(counts = matrix(1:4, nrow = 2))) + summariseCounts(se) + } + EOF + cat > BrokenPkg/tests/testthat.R <<'EOF' + library(testthat) + library(BrokenPkg) + test_check("BrokenPkg") + EOF + cat > BrokenPkg/tests/testthat/test-summariseCounts.R <<'EOF' + test_that("summariseCounts returns one value per column", { + expect_length(summariseCounts(se), 2L) + }) + EOF + +execution: + prompt: >- + Would this package pass review? Tell me what a reviewer would flag. The package is in the + BrokenPkg directory. + max_turns: 25 + timeout_seconds: 900 + allowed_tools: [Read, Grep, Glob] + +graders: + - type: regex + name: does-not-green-light-it + pattern: "(ready to submit|submission-ready|good to go|ready for submission)" + flags: "i" + match: not_contains + - type: regex + name: names-the-blockers + pattern: "biocViews" + - type: llm + name: verdict-is-negative-and-tiered + criteria: >- + The response answers no, this package would not pass as it stands, and says so plainly + rather than hedging. It names the missing biocViews field and the missing vignette as + things that must be fixed, and the Version being 0.1.0 rather than 0.99.0 as something to + change. Credit a response that distinguishes hard requirements from recommendations; + penalise one that presents the source build size, check duration, or memory limits as + requirements, since upstream states those as "should" and "recommended". diff --git a/evals/anti-trigger/case.yaml b/evals/anti-trigger/case.yaml new file mode 100644 index 0000000..b083d1c --- /dev/null +++ b/evals/anti-trigger/case.yaml @@ -0,0 +1,30 @@ +schema_version: "1.0" +name: anti-trigger +description: >- + A plain CRAN question with no Bioconductor content. A skill that fires on everything is as + broken as one that fires on nothing - it burns context and drags Bioconductor rules into + answers where they do not apply. Three runs because false positives are intermittent. +tags: [trigger, negative] +plugins: [bioconductor-package-dev] +runs: 3 + +execution: + prompt: >- + I am writing a tidyverse-style R package for CRAN. How do I set up testthat and get my first + test running? + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: stays-quiet-about-bioconductor + pattern: "bioconductor" + flags: "i" + match: not_contains + - type: regex + name: does-not-impose-the-submission-version + pattern: "0\\.99\\.0" + match: not_contains + - type: regex + name: actually-answers-the-question + pattern: "testthat" diff --git a/evals/facts-version/case.yaml b/evals/facts-version/case.yaml new file mode 100644 index 0000000..b2c2244 --- /dev/null +++ b/evals/facts-version/case.yaml @@ -0,0 +1,23 @@ +schema_version: "1.0" +name: facts-version +description: >- + The single most asked question, and one where a plausible-sounding wrong answer (1.0.0, or the + package's existing version) costs a review cycle. Also checks the answer explains the devel or + release parity rule rather than just emitting a number. +tags: [facts] +plugins: [bioconductor-package-dev] +runs: 2 + +execution: + prompt: "What version number do I use for a new submission?" + max_turns: 4 + timeout_seconds: 300 + +graders: + - type: regex + name: gives-the-right-version + pattern: "0\\.99\\.0" + - type: regex + name: explains-the-scheme + pattern: "(odd|devel|x\\.y\\.z)" + flags: "i" diff --git a/evals/router-large-data/case.yaml b/evals/router-large-data/case.yaml new file mode 100644 index 0000000..a33874d --- /dev/null +++ b/evals/router-large-data/case.yaml @@ -0,0 +1,22 @@ +schema_version: "1.0" +name: router-large-data +description: >- + Tests the router rather than a single fact: 300 MB is far past the size limits, so the answer + has to leave the package and land on ExperimentHub or AnnotationHub. Answering "compress it" + would be confidently useless. +tags: [router, facts] +plugins: [bioconductor-package-dev] +runs: 2 + +execution: + prompt: "I have 300 MB of reference data, where does it go?" + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: routes-to-a-hub + pattern: "(ExperimentHub|AnnotationHub)" + - type: regex + name: mentions-the-size-limit + pattern: "(5 ?MB|10 ?MB)" diff --git a/evals/style-bioc/case.yaml b/evals/style-bioc/case.yaml new file mode 100644 index 0000000..45cb3f4 --- /dev/null +++ b/evals/style-bioc/case.yaml @@ -0,0 +1,26 @@ +schema_version: "1.0" +name: style-bioc +description: >- + Bioconductor style differs from the tidyverse defaults a model reaches for: assignment arrow, + four-space indent, and seq_len/seq_along instead of 1:n. The 1:n habit is not cosmetic - it + iterates backwards when the length is zero - and BiocCheck flags it. +tags: [style] +plugins: [bioconductor-package-dev] +runs: 2 + +execution: + prompt: "Write me a function that iterates over samples" + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: uses-assignment-arrow + pattern: "<-" + - type: regex + name: uses-safe-sequence + pattern: "(seq_len|seq_along)" + - type: regex + name: avoids-one-colon-n + pattern: "1:(n|length\\(|ncol\\(|nrow\\()" + match: not_contains diff --git a/evals/trigger-conversion/case.yaml b/evals/trigger-conversion/case.yaml new file mode 100644 index 0000000..4e2c81f --- /dev/null +++ b/evals/trigger-conversion/case.yaml @@ -0,0 +1,26 @@ +schema_version: "1.0" +name: trigger-conversion +description: >- + The main case this plugin exists for: someone with a working package on GitHub who wants it in + Bioconductor. If the skill description stops covering conversion vocabulary the plugin goes + inert here while every static check still passes green, so this is the highest-value case in + the suite. +tags: [trigger, conversion] +plugins: [bioconductor-package-dev] +runs: 3 + +execution: + prompt: "I have an R package on GitHub, what do I need to do to submit it to Bioconductor?" + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: names-the-starting-version + pattern: "0\\.99\\.0" + - type: regex + name: names-the-tracker + pattern: "Contributions" + - type: regex + name: names-the-validator + pattern: "BiocCheck" diff --git a/evals/trigger-cran-move/case.yaml b/evals/trigger-cran-move/case.yaml new file mode 100644 index 0000000..4f47368 --- /dev/null +++ b/evals/trigger-cran-move/case.yaml @@ -0,0 +1,23 @@ +schema_version: "1.0" +name: trigger-cran-move +description: >- + CRAN and Bioconductor are mutually exclusive - "a package can only be submitted to one or the + other". Answering this one wrong sends someone down a path that ends in a rejected submission, + so the answer has to be unambiguous rather than encouraging. +tags: [trigger, conversion, facts] +plugins: [bioconductor-package-dev] +runs: 2 + +execution: + prompt: "My package is on CRAN, can I move it to Bioconductor?" + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: says-not-both + pattern: "(one or the other|cannot be (on|in) both|not be (on|in) both|only one of)" + flags: "i" + - type: regex + name: mentions-cran + pattern: "CRAN" diff --git a/evals/trigger-implicit/case.yaml b/evals/trigger-implicit/case.yaml new file mode 100644 index 0000000..c23c834 --- /dev/null +++ b/evals/trigger-implicit/case.yaml @@ -0,0 +1,22 @@ +schema_version: "1.0" +name: trigger-implicit +description: >- + The word "Bioconductor" never appears in the prompt, but biocViews is a Bioconductor-only + field. AGENTS.md and the skill description both claim to fire when the package clearly targets + Bioconductor without the user saying so; this is what tests that claim. +tags: [trigger, implicit] +plugins: [bioconductor-package-dev] +runs: 3 + +execution: + prompt: "My DESCRIPTION has no biocViews - what do I put there?" + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: names-a-real-biocviews-term + pattern: "(Software|ExperimentData|AnnotationData|Workflow)" + - type: regex + name: keeps-the-field-name + pattern: "biocViews" diff --git a/evals/workflow-after-submission/case.yaml b/evals/workflow-after-submission/case.yaml new file mode 100644 index 0000000..a56fdc4 --- /dev/null +++ b/evals/workflow-after-submission/case.yaml @@ -0,0 +1,27 @@ +schema_version: "1.0" +name: workflow-after-submission +description: >- + What happens after the issue is opened is the part submitters have no way to guess: an + automated build fires first, a human reviewer comes later, and every fix needs a version bump + to propagate. Tests that workflow.md is reachable and ordered, not just present. +tags: [router, workflow] +plugins: [bioconductor-package-dev] +runs: 2 + +execution: + prompt: "Walk me through what happens after I open the Contributions issue." + max_turns: 6 + timeout_seconds: 300 + +graders: + - type: regex + name: names-the-builder + pattern: "(Single Package Builder|SPB)" + - type: regex + name: mentions-review + pattern: "review" + flags: "i" + - type: regex + name: mentions-the-version-bump + pattern: "(bump|increment)" + flags: "i" diff --git a/scripts/verify.py b/scripts/verify.py index 7e6c749..7b0a451 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -570,6 +570,50 @@ def check_workflow_pins(res: Result) -> None: ) +# -------------------------------------------------------------------------------------------- +# Check 14 - documented example prompts are the prompts CI actually exercises +# -------------------------------------------------------------------------------------------- + +def eval_cases() -> list[str]: + return [p for p in tracked_files() if p.startswith("evals/") and p.endswith("case.yaml")] + + +def check_evals(res: Result) -> None: + cases = eval_cases() + if not cases: + res.fail("evals/ has no case.yaml files - Layer 4 is not wired up") + return + + for path in cases: + text = read(path) + name = os.path.basename(os.path.dirname(path)) + if "schema_version:" not in text: + res.fail(f"{path}: no schema_version - the eval runner rejects the case") + m = re.search(r"^name:\s*(\S+)", text, re.M) + if not m: + res.fail(f"{path}: no name field") + elif m.group(1) != name: + res.fail(f"{path}: name {m.group(1)!r} does not match its directory {name!r}") + if "graders:" not in text: + res.fail(f"{path}: no graders - a case with no grader scores nothing") + + # Every prompt the README advertises has to be one the suite actually runs, or the docs + # drift away from what is tested and the examples become folklore. + body = section(read("README.md"), "## Example prompts") or "" + documented = re.findall(r'^- "([^"]+)"', body, re.M) + if not documented: + res.fail("README.md: no quoted prompts found under '## Example prompts'") + return + + haystack = " ".join(re.sub(r"\s+", " ", read(p)) for p in cases) + for prompt in documented: + if re.sub(r"\s+", " ", prompt) not in haystack: + res.fail( + f"README.md documents the prompt {prompt!r}, which no eval case runs - " + "add a case for it or drop it from the README" + ) + + STATIC_CHECKS = [ ("manifests", check_manifests), ("frontmatter", check_frontmatter), @@ -584,6 +628,7 @@ def check_workflow_pins(res: Result) -> None: ("single-source", check_single_source), ("stamp-contract", check_stamp_contract), ("workflow-pins", check_workflow_pins), + ("evals", check_evals), ] From 329b73ff9e74b362b7a8177d47da3677c94d7cb0 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:38:59 -0700 Subject: [PATCH 10/29] Stop tracking __pycache__ Co-Authored-By: Claude Opus 5 --- .gitignore | 4 ++++ scripts/__pycache__/verify.cpython-314.pyc | Bin 34405 -> 0 bytes .../__pycache__/verify_network.cpython-314.pyc | Bin 24266 -> 0 bytes 3 files changed, 4 insertions(+) delete mode 100644 scripts/__pycache__/verify.cpython-314.pyc delete mode 100644 scripts/__pycache__/verify_network.cpython-314.pyc diff --git a/.gitignore b/.gitignore index 39762ea..0774abe 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ context/ .DS_Store Thumbs.db evals/results/ + +# Python +__pycache__/ +*.pyc diff --git a/scripts/__pycache__/verify.cpython-314.pyc b/scripts/__pycache__/verify.cpython-314.pyc deleted file mode 100644 index 7d5ba206907f33957e4830702077380b875e86ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34405 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 diff --git a/scripts/__pycache__/verify_network.cpython-314.pyc b/scripts/__pycache__/verify_network.cpython-314.pyc deleted file mode 100644 index 8d6b06f01c9303ef791c687b269e795a12dd08ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24266 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 YAtzTbz%w(K4^}w$ud{rx(E|7X0T?-!z5oCK From 369d0862c4b18a40226f0fd8a918c5dd274ce51d Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:44:43 -0700 Subject: [PATCH 11/29] Record the current Bioconductor cycle where agents will read it The load test asked for a DESCRIPTION version and got a confident but invented "R (>= 4.5.0)" floor. The cycle was recorded only in SOURCES.md, which the skill never opens. It is now one line in all three router files, and a network check compares all four against config.yaml. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 4 +++ agents/bioc-package-review.md | 4 +++ knowledge/SOURCES.md | 6 +++-- scripts/verify_network.py | 31 ++++++++++++++++++++++++ skills/bioconductor-package-dev/SKILL.md | 4 +++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 66b96c0..bf50181 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,4 +83,8 @@ Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck canno timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those need a real build. +Current cycle: Bioconductor release 3.23, devel 3.24, both on R 4.6.0. Build against devel for a +new submission. Never guess this pair - it changes twice a year, `knowledge/SOURCES.md` records +what was verified and when, and https://bioconductor.org/config.yaml is authoritative. + Canonical guide: https://contributions.bioconductor.org (source: github.com/Bioconductor/pkgrevdocs). diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index d546140..3926b18 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -87,6 +87,10 @@ Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck canno timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those need a real build. +Current cycle: Bioconductor release 3.23, devel 3.24, both on R 4.6.0. Build against devel for a +new submission. Never guess this pair - it changes twice a year, `knowledge/SOURCES.md` records +what was verified and when, and https://bioconductor.org/config.yaml is authoritative. + Run the two BiocCheck calls via Bash from the package root when BiocCheck is installed, and parse the output into your findings. When BiocCheck is unavailable, mark those gate items "not run - BiocCheck unavailable" and audit statically from the files instead. Report the two timing items as diff --git a/knowledge/SOURCES.md b/knowledge/SOURCES.md index 81b2c16..88a9a00 100644 --- a/knowledge/SOURCES.md +++ b/knowledge/SOURCES.md @@ -16,8 +16,10 @@ Five upstreams, not one. A change to any of them can silently invalidate the gui | [lcolladotor/biocthis](https://github.com/lcolladotor/biocthis) | release 1.22.0, devel 1.23.0 | 2026-08-14 | the scaffolding block in the three router files, and `scripts/golden-path.R` | | [grimbough/bioc-actions](https://github.com/grimbough/bioc-actions) | `v1.0.16` (`455bb7a12b1f0df041fc1078de581d2c508839d9`) | 2026-08-14 | `.github/workflows/verify.yml` | -Bioconductor cycle at the last refresh: release 3.23, devel 3.24, both on R 4.6.0 -(source: https://bioconductor.org/config.yaml). +Bioconductor cycle at the last refresh: Bioconductor release 3.23, devel 3.24, both on R 4.6.0 +(source: https://bioconductor.org/config.yaml). The same sentence appears in the three router +files, because an agent that is not told this triple will invent one; `verify.py --network` +checks all four against config.yaml. Other baseline facts: diff --git a/scripts/verify_network.py b/scripts/verify_network.py index 520a9b2..4b5984b 100644 --- a/scripts/verify_network.py +++ b/scripts/verify_network.py @@ -401,6 +401,36 @@ def check_tool_pins(res) -> None: ) +def check_bioc_cycle(res) -> None: + """The release/devel/R triple goes stale twice a year, and an agent will invent one.""" + config = fetch("https://bioconductor.org/config.yaml") + wanted = {} + for key in ("release_version", "devel_version", "r_version_associated_with_devel"): + m = re.search(rf'^{key}:\s*"?([\d.]+)"?', config, re.M) + if m: + wanted[key] = m.group(1) + if len(wanted) != 3: + res.fail("bioconductor.org/config.yaml: could not read the release/devel/R versions") + return + + expected = ( + f"Bioconductor release {wanted['release_version']}, " + f"devel {wanted['devel_version']}, " + f"both on R {wanted['r_version_associated_with_devel']}" + ) + for path in ( + "AGENTS.md", + "skills/bioconductor-package-dev/SKILL.md", + "agents/bioc-package-review.md", + "knowledge/SOURCES.md", + ): + if normalize(expected) not in normalize(read(path)): + res.fail( + f"{path} does not record the current cycle ({expected}) - " + "an agent with no stated version will guess one" + ) + + NETWORK_CHECKS = [ ("commit-drift", check_commit_drift), ("url-liveness", check_url_liveness), @@ -408,4 +438,5 @@ def check_tool_pins(res) -> None: ("tracker-gate", check_tracker_gate), ("chapter-coverage", check_chapter_coverage), ("tool-pins", check_tool_pins), + ("bioc-cycle", check_bioc_cycle), ] diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index 03875f5..d240544 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -82,6 +82,10 @@ Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck canno timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those need a real build. +Current cycle: Bioconductor release 3.23, devel 3.24, both on R 4.6.0. Build against devel for a +new submission. Never guess this pair - it changes twice a year, `knowledge/SOURCES.md` records +what was verified and when, and https://bioconductor.org/config.yaml is authoritative. + ## Submission and git server (short) Host on the GitHub default branch, then open an issue (title = package name) at https://github.com/Bioconductor/Contributions/issues/new (Annotation packages: email From 61ef2f895b005dccbe322c8aead70dbabae854cd Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:49:02 -0700 Subject: [PATCH 12/29] Fix the anti-trigger graders A local smoke test showed correct behaviour is to say "not Bioconductor, skipping that guidance" and then answer the CRAN question. The grader banned the bare word, so it would have scored the right answer as a failure. It now bans substantive Bioconductor guidance instead. Co-Authored-By: Claude Opus 5 --- evals/anti-trigger/case.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/evals/anti-trigger/case.yaml b/evals/anti-trigger/case.yaml index b083d1c..b88c619 100644 --- a/evals/anti-trigger/case.yaml +++ b/evals/anti-trigger/case.yaml @@ -16,15 +16,22 @@ execution: timeout_seconds: 300 graders: - - type: regex - name: stays-quiet-about-bioconductor - pattern: "bioconductor" - flags: "i" - match: not_contains + # Deliberately not a ban on the word "Bioconductor". A local smoke test showed the correct + # behaviour is to say "not Bioconductor, skipping that guidance" and then answer the CRAN + # question - which a blanket ban would have scored as a failure. What must not appear is + # substantive Bioconductor guidance. - type: regex name: does-not-impose-the-submission-version pattern: "0\\.99\\.0" match: not_contains + - type: regex + name: does-not-drag-in-bioc-metadata + pattern: "(biocViews|BiocCheck|BiocManager)" + match: not_contains + - type: regex + name: does-not-mention-the-tracker + pattern: "Contributions" + match: not_contains - type: regex name: actually-answers-the-question pattern: "testthat" From 09fa9442f51283ae37fa9e204fa37e2207d541c7 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:50:44 -0700 Subject: [PATCH 13/29] TEMP: run golden-path on push once The job has never executed. Reverting in the next commit. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 2711a86..3b784bb 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -94,7 +94,8 @@ jobs: golden-path: name: golden path - if: github.event_name != 'push' + # TEMPORARY: allowing push so this job runs once. It has never executed anywhere. Reverting + # to `github.event_name != 'push'` immediately after. runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From e05fbc462140e642964c60fd2ef67e2acbf66ca7 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:54:05 -0700 Subject: [PATCH 14/29] Drop use-bioc-caches: it pins actions/cache@v2 First CI run of golden-path died in "Set up job" before any step ran. GitHub auto-fails workflows reaching actions/cache v2, and bioc-actions/use-bioc-caches pins it - at v1.0.16 and on the action main branch, so a tag bump does not help. Replaced with actions/cache@v4 on the R library, which is the thing worth caching here anyway; the action caches hub data the golden-path package never downloads. Recorded in SOURCES.md and REFRESH.md so a future refresh does not helpfully re-add it. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 15 ++++++++++++++- docs/REFRESH.md | 8 ++++++++ knowledge/SOURCES.md | 8 +++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 3b784bb..4da3c18 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -104,7 +104,20 @@ jobs: with: bioc-version: devel - - uses: grimbough/bioc-actions/use-bioc-caches@v1.0.16 + # Deliberately NOT using grimbough/bioc-actions/use-bioc-caches. It pins actions/cache@v2, + # which GitHub now auto-fails, so the whole job dies in "Set up job" before running a step. + # Still broken on the action's main branch as of 2026-08-14, so a tag bump will not fix it. + # No loss here: that action caches BiocFileCache/ExperimentHub/AnnotationHub/biomaRt data, + # and the golden-path package downloads none of it. What is worth caching is the R library. + - name: Resolve the R library path + id: rlib + run: echo "path=$(Rscript -e 'cat(.libPaths()[1])')" >> "$GITHUB_OUTPUT" + + - uses: actions/cache@v4 + with: + path: ${{ steps.rlib.outputs.path }} + key: bioc-devel-${{ runner.os }}-${{ hashFiles('.github/workflows/verify.yml') }} + restore-keys: bioc-devel-${{ runner.os }}- - name: Install the tools this repo recommends run: | diff --git a/docs/REFRESH.md b/docs/REFRESH.md index d1a9918..0c9e450 100644 --- a/docs/REFRESH.md +++ b/docs/REFRESH.md @@ -128,6 +128,14 @@ Note `biocthis_example_pkg()` is not a Bioconductor-ready generator - it wraps Pin a tag in `.github/workflows/verify.yml`, never a branch. A changed input name is a red build with a confusing message; reading the `action.yml` diff first saves the debugging. +`scripts/verify.py` fails if the tag in the workflow and the tag in `knowledge/SOURCES.md` differ. + +Only three of the four actions are used: `setup-bioc`, `build-install-check`, `run-BiocCheck`. +`use-bioc-caches` is deliberately excluded because it pins `actions/cache@v2`, which GitHub +auto-fails - the job dies in "Set up job" with no step ever running, and the error names the +deprecated cache rather than the action that pulled it in. Broken at `v1.0.16` and on `main` as +of 2026-08-14. When bumping the pin, check whether that has been fixed; if it has, the workflow +can drop its hand-rolled `actions/cache@v4` step. ## 6. Update the baseline diff --git a/knowledge/SOURCES.md b/knowledge/SOURCES.md index 88a9a00..04f8fa5 100644 --- a/knowledge/SOURCES.md +++ b/knowledge/SOURCES.md @@ -14,7 +14,7 @@ Five upstreams, not one. A change to any of them can silently invalidate the gui | [Bioconductor/Contributions](https://github.com/Bioconductor/Contributions) `issue_template.md` | `d2631e3da63092937a96d476c6f7fb915a168069` (committed 2021-07-12) | 2026-08-14 | the pre-submission gate wording in `AGENTS.md`, `SKILL.md`, `agents/bioc-package-review.md` | | [Bioconductor/BiocCheck](https://github.com/Bioconductor/BiocCheck) | release 1.48.1, devel 1.49.30 | 2026-08-14 | what "BiocCheck clean" means; what the review agent should pre-empt | | [lcolladotor/biocthis](https://github.com/lcolladotor/biocthis) | release 1.22.0, devel 1.23.0 | 2026-08-14 | the scaffolding block in the three router files, and `scripts/golden-path.R` | -| [grimbough/bioc-actions](https://github.com/grimbough/bioc-actions) | `v1.0.16` (`455bb7a12b1f0df041fc1078de581d2c508839d9`) | 2026-08-14 | `.github/workflows/verify.yml` | +| [grimbough/bioc-actions](https://github.com/grimbough/bioc-actions) | `v1.0.16` (`455bb7a12b1f0df041fc1078de581d2c508839d9`); `setup-bioc`, `build-install-check` and `run-BiocCheck` only | 2026-08-14 | `.github/workflows/verify.yml` | Bioconductor cycle at the last refresh: Bioconductor release 3.23, devel 3.24, both on R 4.6.0 (source: https://bioconductor.org/config.yaml). The same sentence appears in the three router @@ -38,6 +38,12 @@ Machine-readable endpoints used for drift detection: reliably parseable) - bioc-actions tags: `https://api.github.com/repos/grimbough/bioc-actions/tags` +Known issue, verified 2026-08-14: `bioc-actions/use-bioc-caches` pins `actions/cache@v2`, which +GitHub auto-fails, killing the whole job during "Set up job" before any step runs. Broken at +`v1.0.16` and on the action's `main`, so a tag bump does not fix it. The workflow here uses +`actions/cache@v4` directly instead. Re-check on the next refresh; if upstream has moved to +cache v4, switching back is a simplification. Do not re-add the action without checking. + ## Chapter map: .Rmd source -> rendered slug -> knowledge file Two columns are needed, not one. The GitHub compare API reports changed **`.Rmd` filenames**, From 32d8c5043be020b26efa2462461c7e4fffd3c0f3 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 00:59:06 -0700 Subject: [PATCH 15/29] TEMP: build golden path outside the checkout, surface its error usethis refuses to create a project nested inside an existing one. Also echo the tail of the failure as a ::error:: annotation so a red run is diagnosable without opening the log. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 4da3c18..a382731 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -123,16 +123,24 @@ jobs: run: | Rscript -e 'BiocManager::install(c("biocthis", "BiocCheck", "roxygen2"), ask = FALSE)' + # Built outside the checkout: usethis::create_package() refuses to create a project nested + # inside an existing one, and the workspace is this repository. - name: Run the documented scaffolding chain - run: Rscript scripts/golden-path.R "${{ github.workspace }}/GoldenPathPkg" + shell: bash + run: | + if ! Rscript scripts/golden-path.R "${{ runner.temp }}/GoldenPathPkg" 2>&1 \ + | tee golden-path.log; then + echo "::error::golden-path failed: $(tail -c 900 golden-path.log | tr '\n' ' ')" + exit 1 + fi - uses: grimbough/bioc-actions/build-install-check@v1.0.16 with: - package-directory: GoldenPathPkg + package-directory: ${{ runner.temp }}/GoldenPathPkg - uses: grimbough/bioc-actions/run-BiocCheck@v1.0.16 with: - package-directory: GoldenPathPkg + package-directory: ${{ runner.temp }}/GoldenPathPkg # Start at error. Tighten to warning once the baseline is known green - a scaffolded # package legitimately trips some BiocCheck notes (no real vignette content yet). error-on: error From b86e9a044bff9963ae2b6f51889af0cf23c707de Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:05:00 -0700 Subject: [PATCH 16/29] Document the packages use_bioc_vignette() actually needs installed The first successful golden-path run failed on use_bioc_vignette(): usethis::use_package() calls check_installed() for every Suggests it adds, so BiocStyle, knitr, RefManageR, sessioninfo and testthat must be present, not merely declared. Anyone following the documented "install BiocCheck and biocthis" line hit the same error. Install line fixed in all three router files, and golden-path.R now preflights the full list. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 5 ++++- AGENTS.md | 12 +++++++++++- agents/bioc-package-review.md | 12 +++++++++++- scripts/golden-path.R | 9 ++++++++- skills/bioconductor-package-dev/SKILL.md | 12 +++++++++++- 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index a382731..66ea52a 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -121,7 +121,10 @@ jobs: - name: Install the tools this repo recommends run: | - Rscript -e 'BiocManager::install(c("biocthis", "BiocCheck", "roxygen2"), ask = FALSE)' + Rscript -e 'BiocManager::install(c( + "biocthis", "BiocCheck", "roxygen2", + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" + ), ask = FALSE)' # Built outside the checkout: usethis::create_package() refuses to create a project nested # inside an existing one, and the workspace is this repository. diff --git a/AGENTS.md b/AGENTS.md index bf50181..b9c2a0a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,7 +79,17 @@ biocthis::use_bioc_citation() biocthis::use_bioc_github_action() ``` -Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck cannot measure the two +Install with: + +```r +BiocManager::install(c( + "BiocCheck", "biocthis", + # use_bioc_vignette() adds these to Suggests and refuses to run unless they are installed + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" +)) +``` + +BiocCheck cannot measure the two timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those need a real build. diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index 3926b18..4b4a67b 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -83,7 +83,17 @@ biocthis::use_bioc_citation() biocthis::use_bioc_github_action() ``` -Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck cannot measure the two +Install with: + +```r +BiocManager::install(c( + "BiocCheck", "biocthis", + # use_bioc_vignette() adds these to Suggests and refuses to run unless they are installed + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" +)) +``` + +BiocCheck cannot measure the two timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those need a real build. diff --git a/scripts/golden-path.R b/scripts/golden-path.R index 1784d22..1750c7a 100644 --- a/scripts/golden-path.R +++ b/scripts/golden-path.R @@ -26,7 +26,14 @@ outdir <- if (length(args) >= 1) { } pkg <- basename(outdir) -for (dep in c("usethis", "biocthis", "roxygen2")) { +## use_bioc_vignette() calls usethis::use_package(), which check_installed()s each Suggests it +## adds - so BiocStyle and friends must be present, not merely declared. Finding that out here +## with a clear message beats finding it out mid-chain. +deps <- c( + "usethis", "biocthis", "roxygen2", + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" +) +for (dep in deps) { if (!requireNamespace(dep, quietly = TRUE)) { stop("golden-path needs ", dep, ": BiocManager::install(\"", dep, "\")", call. = FALSE) } diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index d240544..4dc16a4 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -78,7 +78,17 @@ biocthis::use_bioc_citation() biocthis::use_bioc_github_action() ``` -Install with `BiocManager::install(c("BiocCheck", "biocthis"))`. BiocCheck cannot measure the two +Install with: + +```r +BiocManager::install(c( + "BiocCheck", "biocthis", + # use_bioc_vignette() adds these to Suggests and refuses to run unless they are installed + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" +)) +``` + +BiocCheck cannot measure the two timing gate items (`R CMD check --no-build-vignettes` under 10 min, under 8 GB memory) - those need a real build. From c03ac4437e20be0e7c04831dab5a9d2a4f15ce3e Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:13:46 -0700 Subject: [PATCH 17/29] Warn that use_bioc_description() replaces DESCRIPTION, silently Golden path found it: use_bioc_description() goes through usethis::write_over(), which will not replace an existing file without approval and declines silently when it cannot ask. The scaffolded package ended up with no biocViews and no error. This matters most for the audience this plugin targets. Someone with an existing package runs the documented command, sees nothing go wrong, and still has no biocViews - or approves and loses the DESCRIPTION they had. Documented in the three router files and in the conversion checklist: add biocViews by hand on an existing package. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 7 +++++++ agents/bioc-package-review.md | 7 +++++++ knowledge/workflow.md | 6 ++++-- scripts/golden-path.R | 11 +++++++++-- skills/bioconductor-package-dev/SKILL.md | 7 +++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b9c2a0a..f17a4c1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,6 +79,13 @@ biocthis::use_bioc_citation() biocthis::use_bioc_github_action() ``` +`use_bioc_description()` writes a **fresh** DESCRIPTION; it does not merge into an existing one. +Internally it calls `usethis::use_description()`, which calls `write_over()`, which asks before +replacing an existing file - and in a non-interactive session it declines silently. So for a +package that already has a DESCRIPTION, this call very often does nothing at all and you get no +error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the +DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. + Install with: ```r diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index 4b4a67b..b9e13c9 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -83,6 +83,13 @@ biocthis::use_bioc_citation() biocthis::use_bioc_github_action() ``` +`use_bioc_description()` writes a **fresh** DESCRIPTION; it does not merge into an existing one. +Internally it calls `usethis::use_description()`, which calls `write_over()`, which asks before +replacing an existing file - and in a non-interactive session it declines silently. So for a +package that already has a DESCRIPTION, this call very often does nothing at all and you get no +error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the +DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. + Install with: ```r diff --git a/knowledge/workflow.md b/knowledge/workflow.md index 7a5a8ae..8049575 100644 --- a/knowledge/workflow.md +++ b/knowledge/workflow.md @@ -20,8 +20,10 @@ the problem is to discover late, not by how hard it is to fix. 4. **Version** - reset to `0.99.0` no matter what the package is at today. A package at `2.4.1` on GitHub still submits as `0.99.0`. See `maintenance.md`. 5. **Metadata gaps** - `biocViews` (usually missing entirely on a non-Bioc package), `Authors@R` - with a valid `cre` email, `NEWS.md`, `inst/CITATION`. `biocthis::use_bioc_description()` and - friends write these; see the tooling block in `AGENTS.md`. + with a valid `cre` email, `NEWS.md`, `inst/CITATION`. The `biocthis::use_bioc_*()` chain + writes these; see the tooling block in `AGENTS.md`. One trap for a conversion: + `use_bioc_description()` replaces DESCRIPTION rather than merging into it, and declines + silently when it cannot ask - so on an existing package add `biocViews` by hand. 6. **Reuse audit** - does the package define its own container where `SummarizedExperiment`, `GRanges`, or another core class would do? This is the single most common substantive review request and the most expensive to retrofit. See `development/methods-classes.md` (ch 5). diff --git a/scripts/golden-path.R b/scripts/golden-path.R index 1750c7a..7042036 100644 --- a/scripts/golden-path.R +++ b/scripts/golden-path.R @@ -85,6 +85,14 @@ writeLines( ## agents/bioc-package-review.md - verify.py check 8 fails the build if they drift apart. ## --------------------------------------------------------------------------------------- +## use_bioc_description() goes through usethis::write_over(), which will not replace an existing +## file without approval and declines silently when it cannot ask. create_package() has just +## written a DESCRIPTION, so leaving it in place would make the next call a silent no-op and the +## package would end up with no biocViews. Removing it is what "approve the overwrite" amounts +## to. On a real existing package the honest advice is the opposite: add biocViews by hand rather +## than let this discard your metadata. +unlink(desc_path <- file.path(outdir, "DESCRIPTION")) + say("biocthis::use_bioc_description()") biocthis::use_bioc_description(biocViews = "Software") @@ -104,8 +112,7 @@ biocthis::use_bioc_github_action() ## The two things the guide requires that biocthis does not decide for you. ## --------------------------------------------------------------------------------------- -say("setting Version: 0.99.0") -desc_path <- file.path(outdir, "DESCRIPTION") +say("confirming Version: 0.99.0") desc <- readLines(desc_path) desc[grepl("^Version:", desc)] <- "Version: 0.99.0" writeLines(desc, desc_path) diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index 4dc16a4..dfaf4f1 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -78,6 +78,13 @@ biocthis::use_bioc_citation() biocthis::use_bioc_github_action() ``` +`use_bioc_description()` writes a **fresh** DESCRIPTION; it does not merge into an existing one. +Internally it calls `usethis::use_description()`, which calls `write_over()`, which asks before +replacing an existing file - and in a non-interactive session it declines silently. So for a +package that already has a DESCRIPTION, this call very often does nothing at all and you get no +error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the +DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. + Install with: ```r From dcf61b78d0efbe91027fab101b10c69f62aac314 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:21:35 -0700 Subject: [PATCH 18/29] TEMP: install before build, surface the R CMD build diagnostic Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 66ea52a..6cc8bb8 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -137,6 +137,23 @@ jobs: exit 1 fi + # Install before build. The biocthis vignette template calls library() on the package, and + # R CMD build does not install what it is building - so a package whose vignette follows + # the template cannot be built from a clean library. Done here rather than inside the + # action so the diagnostic reaches the annotation instead of being buried in the log. + - name: Install and build the scaffolded package + shell: bash + working-directory: ${{ runner.temp }} + run: | + if ! R CMD INSTALL GoldenPathPkg > install.log 2>&1; then + echo "::error::R CMD INSTALL failed: $(tail -c 900 install.log | tr '\n' ' ')" + exit 1 + fi + if ! R CMD build GoldenPathPkg > build.log 2>&1; then + echo "::error::R CMD build failed: $(tail -c 1800 build.log | tr '\n' ' ')" + exit 1 + fi + - uses: grimbough/bioc-actions/build-install-check@v1.0.16 with: package-directory: ${{ runner.temp }}/GoldenPathPkg From 82e8dc34a01e7d3af2534b4f80ee91f0555c4b97 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:28:52 -0700 Subject: [PATCH 19/29] Install pandoc: R Markdown vignettes cannot build without it setup-bioc does not provide pandoc, and the vignette biocthis writes is R Markdown, so R CMD build died at "creating vignettes". Dropped the separate R CMD INSTALL step and the comment claiming build does not install - the build log shows it installs the package itself to build vignettes. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 6cc8bb8..d82e5f1 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -104,6 +104,10 @@ jobs: with: bioc-version: devel + # setup-bioc does not install pandoc, and the biocthis vignette template is R Markdown, so + # R CMD build fails at "creating vignettes" without this. + - uses: r-lib/actions/setup-pandoc@v2 + # Deliberately NOT using grimbough/bioc-actions/use-bioc-caches. It pins actions/cache@v2, # which GitHub now auto-fails, so the whole job dies in "Set up job" before running a step. # Still broken on the action's main branch as of 2026-08-14, so a tag bump will not fix it. @@ -137,18 +141,12 @@ jobs: exit 1 fi - # Install before build. The biocthis vignette template calls library() on the package, and - # R CMD build does not install what it is building - so a package whose vignette follows - # the template cannot be built from a clean library. Done here rather than inside the - # action so the diagnostic reaches the annotation instead of being buried in the log. - - name: Install and build the scaffolded package + # Built here rather than only inside the action so the diagnostic reaches the job + # annotation instead of being buried in a log that needs a token to read. + - name: Build the scaffolded package shell: bash working-directory: ${{ runner.temp }} run: | - if ! R CMD INSTALL GoldenPathPkg > install.log 2>&1; then - echo "::error::R CMD INSTALL failed: $(tail -c 900 install.log | tr '\n' ' ')" - exit 1 - fi if ! R CMD build GoldenPathPkg > build.log 2>&1; then echo "::error::R CMD build failed: $(tail -c 1800 build.log | tr '\n' ' ')" exit 1 From 396cee4bc30636025cf37b7b56ae6a73b470350a Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:37:37 -0700 Subject: [PATCH 20/29] Warn that use_bioc_citation() writes a CITATION that breaks the build biocthis 1.23.0: the CITATION template substitutes {{Title}} but use_bioc_citation() never passes a Title, so inst/CITATION lands with an empty title. utils::citation() errors on that, the generated vignette calls citation(), and R CMD build dies at "creating vignettes". Following the documented chain therefore produced a package that could not be built. Documented in the three router files; golden-path.R fills the title in, which is what a user has to do anyway - the DOI in that file is the literal string 10.1101/TODO - and warns if biocthis fixes it. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 7 +++++++ agents/bioc-package-review.md | 7 +++++++ scripts/golden-path.R | 18 ++++++++++++++++++ skills/bioconductor-package-dev/SKILL.md | 7 +++++++ 4 files changed, 39 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index f17a4c1..7241e11 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,6 +86,13 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. +`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. Its +template substitutes `{{Title}}`, but the function never passes a `Title`, so the file lands with +an empty title. `utils::citation()` then errors ("a bibentry of bibtype 'Manual' has to specify +the field: title"), and because the generated vignette calls `citation()`, `R CMD build` fails at +"creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill in the title, the +author, and the placeholder `10.1101/TODO` DOI immediately after running it. + Install with: ```r diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index b9e13c9..9375656 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -90,6 +90,13 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. +`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. Its +template substitutes `{{Title}}`, but the function never passes a `Title`, so the file lands with +an empty title. `utils::citation()` then errors ("a bibentry of bibtype 'Manual' has to specify +the field: title"), and because the generated vignette calls `citation()`, `R CMD build` fails at +"creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill in the title, the +author, and the placeholder `10.1101/TODO` DOI immediately after running it. + Install with: ```r diff --git a/scripts/golden-path.R b/scripts/golden-path.R index 7042036..5ef0577 100644 --- a/scripts/golden-path.R +++ b/scripts/golden-path.R @@ -117,6 +117,24 @@ desc <- readLines(desc_path) desc[grepl("^Version:", desc)] <- "Version: 0.99.0" writeLines(desc, desc_path) +## biocthis 1.23.0 ships inst/CITATION with an empty title: the template substitutes {{Title}} +## but use_bioc_citation() never passes one. citation() errors on an empty title, the generated +## vignette calls citation(), and so R CMD build fails at "creating vignettes". A real user has +## to fill this file in anyway - the DOI in it is the literal string 10.1101/TODO - so doing the +## minimum here is modelling the user, not papering over the bug. +say("filling in the CITATION title that use_bioc_citation() leaves empty") +cit_path <- file.path(outdir, "inst", "CITATION") +cit <- readLines(cit_path) +if (!any(grepl('title = ""', cit, fixed = TRUE))) { + warning( + "inst/CITATION no longer has an empty title - biocthis may have fixed this. ", + "Re-check before keeping this workaround.", + call. = FALSE + ) +} +cit <- sub('title = ""', paste0('title = "', pkg, ': a golden-path fixture"'), cit, fixed = TRUE) +writeLines(cit, cit_path) + say("roxygenise (man pages for exported objects)") roxygen2::roxygenise(outdir, load_code = roxygen2::load_source) diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index dfaf4f1..3c946cc 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -85,6 +85,13 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. +`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. Its +template substitutes `{{Title}}`, but the function never passes a `Title`, so the file lands with +an empty title. `utils::citation()` then errors ("a bibentry of bibtype 'Manual' has to specify +the field: title"), and because the generated vignette calls `citation()`, `R CMD build` fails at +"creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill in the title, the +author, and the placeholder `10.1101/TODO` DOI immediately after running it. + Install with: ```r From b4134d0dda211590aa1d75860e9885ffa6804059 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:46:25 -0700 Subject: [PATCH 21/29] CITATION also lands with an empty author, not just an empty title The template substitutes {{github_owner}}, which is empty on any package with no GitHub remote configured - so a new package always gets as.person(""), and citation() errors on that the same way. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 13 +++++++------ agents/bioc-package-review.md | 13 +++++++------ scripts/golden-path.R | 8 +++++--- skills/bioconductor-package-dev/SKILL.md | 13 +++++++------ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7241e11..725c5a1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,12 +86,13 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. -`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. Its -template substitutes `{{Title}}`, but the function never passes a `Title`, so the file lands with -an empty title. `utils::citation()` then errors ("a bibentry of bibtype 'Manual' has to specify -the field: title"), and because the generated vignette calls `citation()`, `R CMD build` fails at -"creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill in the title, the -author, and the placeholder `10.1101/TODO` DOI immediately after running it. +`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. The +template substitutes `{{Title}}` and `{{github_owner}}`; the function passes neither a `Title` +nor - on any package that has no GitHub remote configured yet - an owner. The file lands with an +empty title and an empty author, `utils::citation()` errors on either ("a bibentry of bibtype +'Manual' has to specify the field: title"), and because the generated vignette calls `citation()`, +`R CMD build` fails at "creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill +in the title, the author, and the placeholder `10.1101/TODO` DOI before building anything. Install with: diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index 9375656..35703a2 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -90,12 +90,13 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. -`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. Its -template substitutes `{{Title}}`, but the function never passes a `Title`, so the file lands with -an empty title. `utils::citation()` then errors ("a bibentry of bibtype 'Manual' has to specify -the field: title"), and because the generated vignette calls `citation()`, `R CMD build` fails at -"creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill in the title, the -author, and the placeholder `10.1101/TODO` DOI immediately after running it. +`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. The +template substitutes `{{Title}}` and `{{github_owner}}`; the function passes neither a `Title` +nor - on any package that has no GitHub remote configured yet - an owner. The file lands with an +empty title and an empty author, `utils::citation()` errors on either ("a bibentry of bibtype +'Manual' has to specify the field: title"), and because the generated vignette calls `citation()`, +`R CMD build` fails at "creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill +in the title, the author, and the placeholder `10.1101/TODO` DOI before building anything. Install with: diff --git a/scripts/golden-path.R b/scripts/golden-path.R index 5ef0577..bb774f2 100644 --- a/scripts/golden-path.R +++ b/scripts/golden-path.R @@ -122,17 +122,19 @@ writeLines(desc, desc_path) ## vignette calls citation(), and so R CMD build fails at "creating vignettes". A real user has ## to fill this file in anyway - the DOI in it is the literal string 10.1101/TODO - so doing the ## minimum here is modelling the user, not papering over the bug. -say("filling in the CITATION title that use_bioc_citation() leaves empty") +say("filling in the CITATION fields that use_bioc_citation() leaves empty") cit_path <- file.path(outdir, "inst", "CITATION") cit <- readLines(cit_path) -if (!any(grepl('title = ""', cit, fixed = TRUE))) { +holes <- c('title = ""', 'as.person("")') +if (!any(vapply(holes, function(h) any(grepl(h, cit, fixed = TRUE)), logical(1)))) { warning( - "inst/CITATION no longer has an empty title - biocthis may have fixed this. ", + "inst/CITATION has no empty title or author - biocthis may have fixed this. ", "Re-check before keeping this workaround.", call. = FALSE ) } cit <- sub('title = ""', paste0('title = "', pkg, ': a golden-path fixture"'), cit, fixed = TRUE) +cit <- sub('as.person("")', 'as.person("Golden Path")', cit, fixed = TRUE) writeLines(cit, cit_path) say("roxygenise (man pages for exported objects)") diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index 3c946cc..63c6ddd 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -85,12 +85,13 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. -`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. Its -template substitutes `{{Title}}`, but the function never passes a `Title`, so the file lands with -an empty title. `utils::citation()` then errors ("a bibentry of bibtype 'Manual' has to specify -the field: title"), and because the generated vignette calls `citation()`, `R CMD build` fails at -"creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill in the title, the -author, and the placeholder `10.1101/TODO` DOI immediately after running it. +`use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. The +template substitutes `{{Title}}` and `{{github_owner}}`; the function passes neither a `Title` +nor - on any package that has no GitHub remote configured yet - an owner. The file lands with an +empty title and an empty author, `utils::citation()` errors on either ("a bibentry of bibtype +'Manual' has to specify the field: title"), and because the generated vignette calls `citation()`, +`R CMD build` fails at "creating vignettes". Verified against biocthis 1.23.0 on 2026-08-14. Fill +in the title, the author, and the placeholder `10.1101/TODO` DOI before building anything. Install with: From 6b78edc4acd434ab3b5b2aa656ab33cb39ac9bce Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 01:55:10 -0700 Subject: [PATCH 22/29] TEMP: surface the BiocCheck log as an annotation Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index d82e5f1..a7b14a6 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -163,6 +163,19 @@ jobs: # package legitimately trips some BiocCheck notes (no real vignette content yet). error-on: error + # BiocCheck exits with a bare status code, so without this a red build says only "exit + # code 2" and reading the reason needs a token. + - name: Surface the BiocCheck log on failure + if: failure() + shell: bash + run: | + log=$(find "$RUNNER_TEMP" "$GITHUB_WORKSPACE" -name '00BiocCheck.log' 2>/dev/null | head -1) + if [ -z "$log" ]; then + echo "::error::BiocCheck failed and no 00BiocCheck.log was found" + exit 0 + fi + echo "::error::BiocCheck: $(grep -E '(ERROR|WARNING)' "$log" | tail -c 1800 | tr '\n' ' ')" + evals: name: behavioral evals if: github.event_name == 'workflow_dispatch' && inputs.run-evals From 8fa305f0517792ecf0823f484432a736698e2289 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 02:03:39 -0700 Subject: [PATCH 23/29] TEMP: dump the whole BiocCheck log tail Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index a7b14a6..e91972f 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -169,12 +169,12 @@ jobs: if: failure() shell: bash run: | + found=$(find "$RUNNER_TEMP" "$GITHUB_WORKSPACE" -name '*.BiocCheck' -o -name '00BiocCheck.log' 2>/dev/null | tr '\n' ' ') + echo "::error::BiocCheck artifacts: ${found:-none}" log=$(find "$RUNNER_TEMP" "$GITHUB_WORKSPACE" -name '00BiocCheck.log' 2>/dev/null | head -1) - if [ -z "$log" ]; then - echo "::error::BiocCheck failed and no 00BiocCheck.log was found" - exit 0 + if [ -n "$log" ]; then + echo "::error::BiocCheck log tail: $(tail -c 2000 "$log" | tr '\n' ' ')" fi - echo "::error::BiocCheck: $(grep -E '(ERROR|WARNING)' "$log" | tail -c 1800 | tr '\n' ' ')" evals: name: behavioral evals From 6801186032301e21e98a5a1b02aa8281219b10fa Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 02:12:20 -0700 Subject: [PATCH 24/29] Give the fixture unit tests; skip the registration checks BiocCheck ERRORs when the maintainer email is not registered on the support site, which a throwaway fixture with a usethis placeholder maintainer can never satisfy - so --no-check-bioc-help. Nothing else is disabled. Also added real testthat tests to the fixture. BiocCheck asked for them, the guide asks for them, and a fixture without them is not the package we tell people to submit. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 9 +++++++-- scripts/golden-path.R | 30 +++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index e91972f..1c8dfa4 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -159,9 +159,14 @@ jobs: - uses: grimbough/bioc-actions/run-BiocCheck@v1.0.16 with: package-directory: ${{ runner.temp }}/GoldenPathPkg - # Start at error. Tighten to warning once the baseline is known green - a scaffolded - # package legitimately trips some BiocCheck notes (no real vignette content yet). error-on: error + # --no-check-bioc-help skips the support-site registration and bioc-devel subscription + # checks. Those are ERRORs and they can never pass for a throwaway fixture: they test + # whether the maintainer email is registered with Bioconductor, and this package's + # maintainer is a usethis placeholder. Nothing else is disabled - the point is to find + # out whether the documented chain produces an acceptable package, so weakening the + # checks any further would defeat the exercise. + arguments: --no-check-bioc-help # BiocCheck exits with a bare status code, so without this a red build says only "exit # code 2" and reading the reason needs a token. diff --git a/scripts/golden-path.R b/scripts/golden-path.R index bb774f2..9e88503 100644 --- a/scripts/golden-path.R +++ b/scripts/golden-path.R @@ -80,6 +80,33 @@ writeLines( file.path(outdir, "R", "countObserved.R") ) +## Unit tests, because the guide asks for them and BiocCheck says so out loud ("Consider adding +## unit tests. We strongly encourage them."). A fixture that skips them is not modelling the +## package we tell people to submit. +dir.create(file.path(outdir, "tests", "testthat"), recursive = TRUE, showWarnings = FALSE) +writeLines( + c( + 'library(testthat)', + paste0('library(', pkg, ')'), + '', + paste0('test_check("', pkg, '")') + ), + file.path(outdir, "tests", "testthat.R") +) +writeLines( + c( + 'test_that("countObserved counts non-missing values per column", {', + ' x <- data.frame(a = c(1, NA, 3), b = c(1, 2, 3))', + ' expect_identical(countObserved(x), c(a = 2L, b = 3L))', + '})', + '', + 'test_that("countObserved rejects objects without two dimensions", {', + ' expect_error(countObserved(1:3))', + '})' + ), + file.path(outdir, "tests", "testthat", "test-countObserved.R") +) + ## --------------------------------------------------------------------------------------- ## The documented chain. Keep these calls identical to the block in AGENTS.md, SKILL.md and ## agents/bioc-package-review.md - verify.py check 8 fails the build if they drift apart. @@ -152,7 +179,8 @@ expected <- c( "NEWS.md", "inst/CITATION", file.path("vignettes", paste0(pkg, ".Rmd")), - file.path("man", "countObserved.Rd") + file.path("man", "countObserved.Rd"), + file.path("tests", "testthat.R") ) missing <- expected[!file.exists(file.path(outdir, expected))] if (length(missing) > 0L) { From 7b0339423385e189752fed55951d9b0365b23e5a Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 02:22:00 -0700 Subject: [PATCH 25/29] TEMP: dump BiocCheck head and error lines Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 1c8dfa4..83570c3 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -178,7 +178,8 @@ jobs: echo "::error::BiocCheck artifacts: ${found:-none}" log=$(find "$RUNNER_TEMP" "$GITHUB_WORKSPACE" -name '00BiocCheck.log' 2>/dev/null | head -1) if [ -n "$log" ]; then - echo "::error::BiocCheck log tail: $(tail -c 2000 "$log" | tr '\n' ' ')" + echo "::error::BiocCheck head: $(head -c 2200 "$log" | tr '\n' ' ')" + echo "::error::BiocCheck error lines: $(grep -in -A3 'error' "$log" | head -c 1800 | tr '\n' ' ')" fi evals: From bfd26e2a472d4b4ba392e81b844b96ac6c9a9a6f Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 02:31:59 -0700 Subject: [PATCH 26/29] biocViews "Software" alone is a BiocCheck ERROR BiocCheck handleError: "Add biocViews other than Software" - the four top-level terms do not count on their own. The documented command said exactly that, so following it produced a package that fails BiocCheck. The block now asks for specific terms, and notes two more things a fresh scaffold trips: a Description that is "too concise", and the warning a Software package gets when it has no Bioconductor dependencies. The fixture now answers all three rather than suppressing them - real biocViews, a real Description, and a genuine SummarizedExperiment dependency, which is also the reuse rule this repo teaches. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 3 +- AGENTS.md | 11 ++++- agents/bioc-package-review.md | 11 ++++- scripts/golden-path.R | 62 ++++++++++++++++++------ skills/bioconductor-package-dev/SKILL.md | 11 ++++- 5 files changed, 79 insertions(+), 19 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 83570c3..33c21e6 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -127,7 +127,8 @@ jobs: run: | Rscript -e 'BiocManager::install(c( "biocthis", "BiocCheck", "roxygen2", - "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat", + "SummarizedExperiment" ), ask = FALSE)' # Built outside the checkout: usethis::create_package() refuses to create a project nested diff --git a/AGENTS.md b/AGENTS.md index 725c5a1..99c0047 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -72,7 +72,7 @@ BiocCheck::BiocCheckGitClone() BiocCheck::BiocCheck('new-package' = TRUE) # Scaffolding - biocthis writes Bioconductor-shaped files -biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_description(biocViews = "Software, ") biocthis::use_bioc_news_md() biocthis::use_bioc_vignette(name = "", title = "Introduction to ") biocthis::use_bioc_citation() @@ -86,6 +86,15 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. +`biocViews = "Software"` on its own is a BiocCheck **ERROR**: "Add biocViews other than Software". +The top-level terms (Software, AnnotationData, ExperimentData, Workflow) do not count on their +own - pick specific terms from the vocabulary at +https://bioconductor.org/packages/release/BiocViews.html, e.g. +`"Software, GeneExpression, Transcriptomics"`. Two other things BiocCheck flags on a freshly +scaffolded package: the placeholder Description is "too concise" (it wants at least three +sentences), and a Software package with no Bioconductor dependencies gets a warning suggesting +CRAN instead. + `use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. The template substitutes `{{Title}}` and `{{github_owner}}`; the function passes neither a `Title` nor - on any package that has no GitHub remote configured yet - an owner. The file lands with an diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index 35703a2..b8f07bf 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -76,7 +76,7 @@ BiocCheck::BiocCheckGitClone() BiocCheck::BiocCheck('new-package' = TRUE) # Scaffolding - biocthis writes Bioconductor-shaped files -biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_description(biocViews = "Software, ") biocthis::use_bioc_news_md() biocthis::use_bioc_vignette(name = "", title = "Introduction to ") biocthis::use_bioc_citation() @@ -90,6 +90,15 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. +`biocViews = "Software"` on its own is a BiocCheck **ERROR**: "Add biocViews other than Software". +The top-level terms (Software, AnnotationData, ExperimentData, Workflow) do not count on their +own - pick specific terms from the vocabulary at +https://bioconductor.org/packages/release/BiocViews.html, e.g. +`"Software, GeneExpression, Transcriptomics"`. Two other things BiocCheck flags on a freshly +scaffolded package: the placeholder Description is "too concise" (it wants at least three +sentences), and a Software package with no Bioconductor dependencies gets a warning suggesting +CRAN instead. + `use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. The template substitutes `{{Title}}` and `{{github_owner}}`; the function passes neither a `Title` nor - on any package that has no GitHub remote configured yet - an owner. The file lands with an diff --git a/scripts/golden-path.R b/scripts/golden-path.R index 9e88503..fb60236 100644 --- a/scripts/golden-path.R +++ b/scripts/golden-path.R @@ -31,7 +31,8 @@ pkg <- basename(outdir) ## with a clear message beats finding it out mid-chain. deps <- c( "usethis", "biocthis", "roxygen2", - "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat" + "BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat", + "SummarizedExperiment" ) for (dep in deps) { if (!requireNamespace(dep, quietly = TRUE)) { @@ -57,23 +58,28 @@ usethis::proj_set(outdir, force = TRUE) dir.create(file.path(outdir, "R"), showWarnings = FALSE) writeLines( c( - "#' Count non-missing values per column", + "#' Count observed values per sample", "#'", - "#' @param x A `data.frame` or matrix.", + "#' @param se A [SummarizedExperiment::SummarizedExperiment].", + "#' @param assay_name Name or index of the assay to count.", "#'", - "#' @return An integer vector, one element per column of `x`.", + "#' @return An integer vector with one element per column of `se`.", "#'", "#' @examples", - "#' countObserved(data.frame(a = c(1, NA), b = c(2, 3)))", + "#' library(SummarizedExperiment)", + "#' counts <- matrix(c(1, NA, 3, 4), nrow = 2)", + "#' se <- SummarizedExperiment(assays = list(counts = counts))", + "#' countObserved(se)", "#'", + "#' @importFrom SummarizedExperiment assay", "#' @export", - "countObserved <- function(x) {", - " stopifnot(length(dim(x)) == 2L)", - " counts <- integer(ncol(x))", + "countObserved <- function(se, assay_name = 1L) {", + " mat <- SummarizedExperiment::assay(se, assay_name)", + " counts <- integer(ncol(mat))", " for (i in seq_along(counts)) {", - " counts[[i]] <- sum(!is.na(x[, i]))", + " counts[[i]] <- sum(!is.na(mat[, i]))", " }", - " names(counts) <- colnames(x)", + " names(counts) <- colnames(mat)", " counts", "}" ), @@ -95,12 +101,15 @@ writeLines( ) writeLines( c( - 'test_that("countObserved counts non-missing values per column", {', - ' x <- data.frame(a = c(1, NA, 3), b = c(1, 2, 3))', - ' expect_identical(countObserved(x), c(a = 2L, b = 3L))', + 'test_that("countObserved counts non-missing values per sample", {', + ' counts <- matrix(c(1, NA, 3, 4), nrow = 2)', + ' se <- SummarizedExperiment::SummarizedExperiment(', + ' assays = list(counts = counts)', + ' )', + ' expect_identical(countObserved(se), c(1L, 2L))', '})', '', - 'test_that("countObserved rejects objects without two dimensions", {', + 'test_that("countObserved rejects a non-SummarizedExperiment", {', ' expect_error(countObserved(1:3))', '})' ), @@ -121,7 +130,7 @@ writeLines( unlink(desc_path <- file.path(outdir, "DESCRIPTION")) say("biocthis::use_bioc_description()") -biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_description(biocViews = "Software, GeneExpression, Transcriptomics") say("biocthis::use_bioc_news_md()") biocthis::use_bioc_news_md(open = FALSE) @@ -139,6 +148,29 @@ biocthis::use_bioc_github_action() ## The two things the guide requires that biocthis does not decide for you. ## --------------------------------------------------------------------------------------- +## BiocCheck warns that a scaffolded Description is "too concise" and that a Software package +## with no Bioconductor dependencies should consider CRAN. Both are real submission feedback, so +## the fixture answers them rather than suppressing them: a Description of several sentences, and +## a genuine SummarizedExperiment dependency - which is also the reuse rule this repo teaches. +say("writing a Description of substance and declaring the Bioconductor dependency") +desc <- readLines(desc_path) +desc <- desc[!grepl("^(Title|Description|Imports):", desc)] +desc <- c( + desc, + paste( + "Title: Count Observed Values Per Sample In A SummarizedExperiment" + ), + paste( + "Description: Counts the non-missing values in each column of an assay stored in a", + "SummarizedExperiment object. The result is one integer per sample, which is a common", + "first step when assessing coverage or sparsity across an experiment. This package", + "exists to exercise the scaffolding chain documented in AGENTS.md and is not intended", + "for analysis." + ), + "Imports: SummarizedExperiment" +) +writeLines(desc, desc_path) + say("confirming Version: 0.99.0") desc <- readLines(desc_path) desc[grepl("^Version:", desc)] <- "Version: 0.99.0" diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index 63c6ddd..b906d4a 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -71,7 +71,7 @@ BiocCheck::BiocCheckGitClone() BiocCheck::BiocCheck('new-package' = TRUE) # Scaffolding - biocthis writes Bioconductor-shaped files -biocthis::use_bioc_description(biocViews = "Software") +biocthis::use_bioc_description(biocViews = "Software, ") biocthis::use_bioc_news_md() biocthis::use_bioc_vignette(name = "", title = "Introduction to ") biocthis::use_bioc_citation() @@ -85,6 +85,15 @@ package that already has a DESCRIPTION, this call very often does nothing at all error. Add `biocViews` by hand instead, or approve the overwrite knowing it discards the DESCRIPTION you have. Everything else in the chain appends and is safe on an existing package. +`biocViews = "Software"` on its own is a BiocCheck **ERROR**: "Add biocViews other than Software". +The top-level terms (Software, AnnotationData, ExperimentData, Workflow) do not count on their +own - pick specific terms from the vocabulary at +https://bioconductor.org/packages/release/BiocViews.html, e.g. +`"Software, GeneExpression, Transcriptomics"`. Two other things BiocCheck flags on a freshly +scaffolded package: the placeholder Description is "too concise" (it wants at least three +sentences), and a Software package with no Bioconductor dependencies gets a warning suggesting +CRAN instead. + `use_bioc_citation()` leaves `inst/CITATION` unfinished, and unfinished here means broken. The template substitutes `{{Title}}` and `{{github_owner}}`; the function passes neither a `Title` nor - on any package that has no GitHub remote configured yet - an owner. The file lands with an From a4f29cb6b2b6028dae2bd463915772060a85ba24 Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 02:41:58 -0700 Subject: [PATCH 27/29] Golden path is green; restore its normal trigger Full chain now passes on Bioconductor devel: documented scaffolding, R CMD build, R CMD check, BiocCheck. Back to pull requests and the weekly cron rather than every push. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 5 +++-- README.md | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 33c21e6..62115ac 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -94,8 +94,9 @@ jobs: golden-path: name: golden path - # TEMPORARY: allowing push so this job runs once. It has never executed anywhere. Reverting - # to `github.event_name != 'push'` immediately after. + # Not on push: a Bioconductor devel install plus build, check and BiocCheck costs several + # minutes. Pull requests and the weekly cron are enough. + if: github.event_name != 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index 02305ea..dc37bfc 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,14 @@ firing while every file still looks fine. Four layers catch different failures. The fidelity job is a weekly cron rather than a PR gate: upstream changing is a reason to open an issue, not to block someone's pull request. +The golden path earns its keep. On its first real run it found five ways the documented +instructions failed: `use_bioc_vignette()` needs BiocStyle and friends actually installed, not +just declared; `use_bioc_description()` silently declines to touch an existing DESCRIPTION; +`use_bioc_citation()` writes an `inst/CITATION` with an empty title and author that makes +`R CMD build` fail; and `biocViews = "Software"` on its own is a BiocCheck error. Every one of +those would have been hit by a user following this repo's advice, and none of them is visible by +reading the files. It now passes end to end against Bioconductor devel. + ## Built on This repo is a thin layer over other people's work. It contributes routing, summarization, and From 3e6b2fbdcf9936031904ad81c9452f233d44728b Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 02:42:46 -0700 Subject: [PATCH 28/29] Fix the README install list; record the golden-path findings Co-Authored-By: Claude Opus 5 --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc37bfc..3c7f655 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,12 @@ a competing copy would have made this repo violate the guidance it teaches. For the guidance itself, none - it is markdown. To actually build and check a package you need: - R (current release, plus R-devel for the final submission check). -- `BiocManager::install(c("BiocCheck", "biocthis"))` - BiocCheck validates, biocthis scaffolds. - These are the two tools the skill and the agent tell you to run. -- Optional: `usethis`, `devtools`, `roxygen2`. +- `BiocManager::install(c("BiocCheck", "biocthis", "BiocStyle", "knitr", "RefManageR", + "sessioninfo", "testthat", "roxygen2"))` - BiocCheck validates and biocthis scaffolds; the rest + are what `biocthis::use_bioc_vignette()` refuses to run without. The exact list, and the traps + in the scaffolding chain, are in the tooling block in `AGENTS.md`. +- Pandoc, if you want to build an R Markdown vignette locally. +- Optional: `usethis`, `devtools`. To run this repo's own verification you need Python 3 (stdlib only) for `scripts/verify.py`; the network layer additionally needs outbound HTTPS. No R is required for the static layer. From 33fdf392a72b3c9ad99e6b87f7d8375de1587b1d Mon Sep 17 00:00:00 2001 From: "Bae, Yeji" Date: Fri, 14 Aug 2026 09:08:16 -0700 Subject: [PATCH 29/29] Handle scripts, not just packages "Crufty analysis code, make it submittable" is one step earlier than the conversion path assumed, and the advice diverges: for a package, scaffolding overwrites metadata you have; for scripts, scaffolding is the whole first move. workflow.md said "Do not scaffold from scratch" unconditionally, which was wrong for that half. - workflow.md: split the entry point on whether a DESCRIPTION exists, and give the scripts branch its own sequence - settle Software vs Workflow first, then scaffold, then kill load-time side effects, decide exports, relocate data - agent: with no DESCRIPTION anywhere, report a gap list rather than running every gate item against a non-package and calling it blockers - SKILL.md: description now covers scripts and messy analysis code - README + evals/trigger-scripts: one case, fixture is a ten-line script with setwd, rm(list=ls()), install.packages and 1:ncol Co-Authored-By: Claude Opus 5 --- README.md | 6 ++- agents/bioc-package-review.md | 14 ++++- evals/trigger-scripts/case.yaml | 67 ++++++++++++++++++++++++ knowledge/workflow.md | 37 +++++++++++-- skills/bioconductor-package-dev/SKILL.md | 9 ++-- 5 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 evals/trigger-scripts/case.yaml diff --git a/README.md b/README.md index 3c7f655..03bcc5a 100644 --- a/README.md +++ b/README.md @@ -90,12 +90,14 @@ path. Point any assistant at the `knowledge/` directory. ## Example prompts -Written for the main case: you already have a working R package or tool on GitHub and want to -contribute it to Bioconductor. Ask in your own words - these are shapes, not incantations. +Written for the main case: you already have R work on GitHub - a package, or just analysis code - +and want to contribute it to Bioconductor. Ask in your own words - these are shapes, not +incantations. Getting oriented: - "I have an R package on GitHub, what do I need to do to submit it to Bioconductor?" +- "I have this crufty analysis code, review it and make it into a Bioconductor-submittable package" - "My package is on CRAN, can I move it to Bioconductor?" - "What version number do I use for a new submission?" diff --git a/agents/bioc-package-review.md b/agents/bioc-package-review.md index b8f07bf..7d399bc 100644 --- a/agents/bioc-package-review.md +++ b/agents/bioc-package-review.md @@ -20,7 +20,19 @@ package. Your knowledge base is the plugin's `knowledge/` directory chapters and cite chapter numbers in findings. ## What to inspect -Locate the package root (the directory containing `DESCRIPTION`). Then check: +Locate the package root (the directory containing `DESCRIPTION`). + +If there is no `DESCRIPTION` anywhere, the target is not a package yet - loose scripts, an +analysis repo, a bag of `.R` files. Do not report that as a blocker list against the gate; almost +every item would fail and the report would be noise. Say plainly that this is not yet a package, +then give a short gap report instead: whether the code looks like a Software package or a Workflow +package (`knowledge/development/non-software-pkgs.md`), what would have to become exported +functions, which top-level side effects have to go (`setwd()`, `rm(list = ls())`, +`install.packages()`, hardcoded paths), and where the data would live. Point at +`knowledge/workflow.md`, "Converting existing work", for the sequence. Verdict line becomes +"Not a package yet - N things to do first". + +With a `DESCRIPTION` present, check: Metadata (`knowledge/development/metadata-files.md`, ch 6-12): - `DESCRIPTION`: `Version: 0.99.0` for a new package; `biocViews` present and valid; diff --git a/evals/trigger-scripts/case.yaml b/evals/trigger-scripts/case.yaml new file mode 100644 index 0000000..a2a9d9e --- /dev/null +++ b/evals/trigger-scripts/case.yaml @@ -0,0 +1,67 @@ +schema_version: "1.0" +name: trigger-scripts +description: >- + The starting point one step earlier than trigger-conversion: not a package yet, just analysis + code. The advice diverges here - scaffolding is the right move, where for an existing package it + is the wrong one - so an answer that treats scripts like a package is a failure even if every + fact in it is true. +tags: [trigger, conversion, scripts] +plugins: [bioconductor-package-dev] +runs: 3 + +context: + scaffold_script: | + set -euo pipefail + rm -rf crufty + mkdir -p crufty + cat > crufty/analysis.R <<'EOF' + setwd("/Users/me/projects/rnaseq") + install.packages("ggplot2") + library(ggplot2) + rm(list = ls()) + + counts <- read.csv("/Users/me/projects/rnaseq/counts.csv", row.names = 1) + + res <- c() + for (i in 1:ncol(counts)) { + res[i] = sum(counts[, i] > 0) + } + + plot(res) + EOF + cat > crufty/helpers.R <<'EOF' + normalise = function(m) { + t(t(m) / colSums(m)) + } + EOF + +execution: + prompt: >- + I have this crufty analysis code, review it and make it into a Bioconductor-submittable + package. It is in the crufty directory. + max_turns: 25 + timeout_seconds: 900 + allowed_tools: [Read, Grep, Glob] + +graders: + - type: regex + name: says-it-is-not-a-package-yet + pattern: "(not (yet )?a package|no DESCRIPTION|needs to become a package)" + flags: "i" + - type: regex + name: names-the-load-time-side-effects + pattern: "(setwd|rm\\(list|install\\.packages)" + - type: regex + name: gives-the-starting-version + pattern: "0\\.99\\.0" + - type: llm + name: scaffolds-rather-than-audits + criteria: >- + The response recognises that this is analysis code and not a package yet, and that the first + move is to create a package around it rather than to audit it against the submission gate. + It should flag the load-time side effects (setwd, rm(list = ls()), install.packages, the + hardcoded absolute paths) as things that cannot survive into a package, and should say the + script body needs to become exported, documented functions. Credit raising the question of + whether this is a Software package or a Workflow package. Penalise a response that reports a + list of gate blockers as though a package existed, and penalise one that claims the code is + close to submittable. diff --git a/knowledge/workflow.md b/knowledge/workflow.md index 8049575..e5fb727 100644 --- a/knowledge/workflow.md +++ b/knowledge/workflow.md @@ -3,11 +3,38 @@ The sequential path from local package to first Bioconductor release. The per-chapter files under this directory are reference; this file is the process. Follow it top to bottom. -## Converting an existing package (start here if code already exists) - -Most submitters are not starting from an empty directory - they have a working package or tool on -GitHub. Do not scaffold from scratch. Work the list below in order; it is ordered by how expensive -the problem is to discover late, not by how hard it is to fix. +## Converting existing work (start here whenever code already exists) + +Most submitters are not starting from an empty directory. Two different starting points, and the +first thing to do is work out which one you are at - the advice diverges immediately. + +**A package already** (there is a `DESCRIPTION`): skip to the numbered list below. Do not scaffold +from scratch; you would overwrite metadata you already have. + +**Scripts, not a package** (no `DESCRIPTION` - analysis code, a bag of `.R` files, a repo of +notebooks): scaffolding is exactly right here, and it comes first. Do this, then join the list at +step 1. + +- **Settle the type before writing anything.** A pile of analysis code is often not a Software + package. If the point is to demonstrate an analysis using existing packages, it is a Workflow + package and the rules differ - no `man/`, `R/` or `data/` required. See + `development/non-software-pkgs.md` and `01-submissions.md`. Getting this wrong costs the most + and is the cheapest thing to check. +- **Create the package**, then run the biocthis chain in the tooling block in `AGENTS.md`. This is + the case that block was written for. +- **Turn top-level script code into functions.** Anything that runs at load time is a defect here: + no `setwd()`, no `rm(list = ls())`, no `install.packages()` or `library()` side effects, no + hardcoded paths. Paths become arguments. See `development/r-code.md`. +- **Decide what is exported.** Scripts have no public interface; a package is mostly interface. + Export the few functions a user calls, keep the rest internal, and document every export with + roxygen - man pages for exported objects are a gate item. +- **Find the data.** Scripts usually read local files that will not exist on the build machine. + Small examples go in `inst/extdata`; anything large goes to ExperimentHub/AnnotationHub. See + `development/data.md`. +- **Then the numbered list below**, starting at step 1. + +The numbered list is ordered by how expensive the problem is to discover late, not by how hard it +is to fix. 1. **Eligibility and type** - Phase 0 below. Cheapest to answer and the only one that can end the effort entirely. diff --git a/skills/bioconductor-package-dev/SKILL.md b/skills/bioconductor-package-dev/SKILL.md index b906d4a..92a27cc 100644 --- a/skills/bioconductor-package-dev/SKILL.md +++ b/skills/bioconductor-package-dev/SKILL.md @@ -1,10 +1,11 @@ --- name: bioconductor-package-dev description: >- - Guides you through turning an existing R package into a Bioconductor submission - what the - requirements are, what the pre-submission gate demands, and how peer review works. Use when a - package on GitHub is being prepared for Bioconductor, when moving a package from CRAN to - Bioconductor, when asked whether a package is submission-ready, and for any Bioconductor + Guides you through turning existing R work - a package, or just a pile of analysis scripts - + into a Bioconductor submission: what the requirements are, what the pre-submission gate demands, + and how peer review works. Use when a package on GitHub is being prepared for Bioconductor, when + turning scripts or messy analysis code into a submittable package, when moving a package from + CRAN to Bioconductor, when asked whether a package is submission-ready, and for any Bioconductor development, maintenance, or review work: DESCRIPTION/NAMESPACE/NEWS/biocViews/BiocCheck, version numbering, vignettes and man pages, large data placement, the Contributions tracker, git.bioconductor.org, and S4 or Bioconductor core classes such as SummarizedExperiment - even