Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
22ce594
Add Bioconductor package-dev cross-tool skill, review agent, and know…
ybaeus Jul 23, 2026
b1084c6
Untrack context/ working docs; add .gitignore
ybaeus Jul 23, 2026
9657529
Fix README layout tree: drop untracked context/, add knowledge/SOURCE…
ybaeus Jul 24, 2026
c3e201e
Add docs/REFRESH.md and README example prompts
ybaeus Aug 14, 2026
d193a0d
Defer to BiocCheck and biocthis; fix over-hardened rules
ybaeus Aug 14, 2026
ff206b6
Add scripts/verify.py; split the gate into requirement and recommenda…
ybaeus Aug 14, 2026
aa64365
Add the upstream fidelity layer (verify.py --network)
ybaeus Aug 14, 2026
ccd87ce
Add the golden path and wire all layers to CI
ybaeus Aug 14, 2026
4beb46f
Add the behavioral eval suite
ybaeus Aug 14, 2026
329b73f
Stop tracking __pycache__
ybaeus Aug 14, 2026
369d086
Record the current Bioconductor cycle where agents will read it
ybaeus Aug 14, 2026
61ef2f8
Fix the anti-trigger graders
ybaeus Aug 14, 2026
09fa944
TEMP: run golden-path on push once
ybaeus Aug 14, 2026
e05fbc4
Drop use-bioc-caches: it pins actions/cache@v2
ybaeus Aug 14, 2026
32d8c50
TEMP: build golden path outside the checkout, surface its error
ybaeus Aug 14, 2026
b86e9a0
Document the packages use_bioc_vignette() actually needs installed
ybaeus Aug 14, 2026
c03ac44
Warn that use_bioc_description() replaces DESCRIPTION, silently
ybaeus Aug 14, 2026
dcf61b7
TEMP: install before build, surface the R CMD build diagnostic
ybaeus Aug 14, 2026
82e8dc3
Install pandoc: R Markdown vignettes cannot build without it
ybaeus Aug 14, 2026
396cee4
Warn that use_bioc_citation() writes a CITATION that breaks the build
ybaeus Aug 14, 2026
b4134d0
CITATION also lands with an empty author, not just an empty title
ybaeus Aug 14, 2026
6b78edc
TEMP: surface the BiocCheck log as an annotation
ybaeus Aug 14, 2026
8fa305f
TEMP: dump the whole BiocCheck log tail
ybaeus Aug 14, 2026
6801186
Give the fixture unit tests; skip the registration checks
ybaeus Aug 14, 2026
7b03394
TEMP: dump BiocCheck head and error lines
ybaeus Aug 14, 2026
bfd26e2
biocViews "Software" alone is a BiocCheck ERROR
ybaeus Aug 14, 2026
a4f29cb
Golden path is green; restore its normal trigger
ybaeus Aug 14, 2026
3e6b2fb
Fix the README install list; record the golden-path findings
ybaeus Aug 14, 2026
33fdf39
Handle scripts, not just packages
ybaeus Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -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."
}
]
}
11 changes: 11 additions & 0 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
}
223 changes: 223 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
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
# 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

- uses: grimbough/bioc-actions/setup-bioc@v1.0.16
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.
# 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: |
Rscript -e 'BiocManager::install(c(
"biocthis", "BiocCheck", "roxygen2",
"BiocStyle", "knitr", "RefManageR", "sessioninfo", "testthat",
"SummarizedExperiment"
), 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
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

# 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 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

- uses: grimbough/bioc-actions/run-BiocCheck@v1.0.16
with:
package-directory: ${{ runner.temp }}/GoldenPathPkg
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.
- name: Surface the BiocCheck log on failure
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 [ -n "$log" ]; then
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:
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 }}
# --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
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Working docs - local only, not shipped
context/

# OS
.DS_Store
Thumbs.db
evals/results/

# Python
__pycache__/
*.pyc
124 changes: 124 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# 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
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
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`.

## 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, <two or more specific terms>")
biocthis::use_bioc_news_md()
biocthis::use_bioc_vignette(name = "<pkg>", title = "Introduction to <pkg>")
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.

`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
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:

```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.

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).
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading