Keep CLAUDE.md off the pkgdown site (#42) - #43
Merged
NewGraphEnvironment merged 2 commits intoAug 31, 2026
Conversation
pkgdown renders every root-level .md except a hardcoded allowlist, with no config option to exclude one, so `CLAUDE.html` has been live on the public site. `.Rbuildignore` does not reach it — that governs `R CMD build`, not the docs. Three changes, and the first alone would not have worked: - `rm -f CLAUDE.md` before `build_site()`, so none of the three copies is produced. There are three, not one: the rendered page, a verbatim copy of the source served as-is, and the full-text search index (227 hits for CLAUDE). - An allowlist gate after the build, because the point is to close the mechanism rather than delete one file. Without it the next root-level .md anyone adds leaks silently, which is how this went unnoticed. - `clean: true` on the deploy. With `clean: false` the action never deletes, so every file ever deployed stays on gh-pages whether or not the source still produces it — removing CLAUDE.md from the build would have left the published copies exactly where they were. Checked before flipping: no CNAME on gh-pages, no dev/ versioned docs, and docs/ is untracked so nothing there is hand-added. Verified rather than reasoned. `pkgdown:::package_mds()` returns `CLAUDE.md` and nothing else for this repo, and returns none once the file is gone. The gate was extracted verbatim from the YAML that ships and run against five cases: a clean site (0), CLAUDE.html present (1), only CLAUDE.md present (1), both copies (1, named once), and an empty docs/ (1). That last one is the trap — a loop over nothing exits 0, so without an explicit check a build that produced no site at all would have reported "all declared". Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GBKqedyBysV7hB4DuL98ZR
`case " $allowed " in *" $b "*)` reads like a token test and is not — it matches a contiguous substring of the list, so a root page named "authors index" passed. Verified against the shipped script before fixing: exit 0 where it must be 1. Stripping both suffixes unconditionally had the same shape, reducing "index.md.html" to "index". Neither is reachable through pkgdown today, and `R CMD check` rejects a filename containing a space, so this is a guard that was weaker than it read rather than a live leak. That is worth fixing anyway: the whole value of this step is that someone trusts it a year from now. Now an explicit token loop, with the suffix that actually matched stripped. Re-tested across nine cases, extracted verbatim from the YAML rather than retyped: clean site (0), CLAUDE.html (1), CLAUDE.md (1), both (1), empty docs/ (1), the substring bypass (1), a double suffix (1), a glob metacharacter in a filename (1), and a plausible future root page (1). Correction to the previous commit message, which cannot be edited: the second published copy is a pandoc-regenerated markdown twin, not a verbatim copy of the source. The content is published either way, so the three-copy count and the remedy are unchanged. The issue and PR bodies have been corrected. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GBKqedyBysV7hB4DuL98ZR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #42.
CLAUDE.htmlhas been live on fly's public pkgdown site. pkgdown renders everyroot-level
.mdexcept a hardcoded allowlist, and there is no config option to excludeone —
.RbuildignoregovernsR CMD build, not the docs site.Three changes, and the first alone would not have worked
rm -f CLAUDE.mdbeforebuild_site()CLAUDE). Deleting onlyCLAUDE.htmllooks like a fix and achieves nothing.mdanyone adds leaks silently — which is how this went unnoticed hereclean: trueon the deployclean: falsethe action never deletes, so removing the file from the build would have left the published copies exactly where they areChecked before flipping
cleanclean: truedeletes anything ongh-pagesnot present indocs/, so:CNAME(404) — the custom domain comes from the org site repo, nothing to breakdev/— no versioned docs to losedocs/is untracked (0 files in git), so the whole site is CI-generated and nothingon the branch is a hand-added asset
Verified, not reasoned
pkgdown:::package_mds()— the function that actually makes this decision — returnsCLAUDE.mdand nothing else for this repo, and returns none once the file is gone.The gate was extracted verbatim from the YAML that ships rather than retyped, and run
against five cases:
CLAUDE.htmlpresentCLAUDE.mdpresentdocs/authors indexindex.md.htmlNOTES)Two of these were failures the first time. The gate went through two rounds, and
neither weakness was visible by reading:
docs/*.htmland exited 0 on an emptydocs/— anaffirmative claim of success about a build that produced nothing
case " $allowed " in *" $b "*), which reads like a token test and isa substring test, so
authors indexpassed. Stripping both suffixesunconditionally had the same shape, reducing
index.md.htmltoindexBoth fail toward pass. Neither is reachable through pkgdown today —
R CMD checkrejects a filename with a space — so this was a guard weaker than it read rather than a
live leak. Worth fixing anyway, since the entire value of the step is that someone trusts
it a year from now. A loop over nothing exits 0, so without an explicit
population check a build that produced no site at all would have reported "Root pages are
all declared" — a guard making an affirmative claim of success about work that never
happened. The gate now fails on a missing
docs/index.htmlfirst.The
pull_requesttrigger runs the build and the gate (deploy is skipped), so this PR'sown CI exercises both.
Not a release
.github/is in.Rbuildignore, so the built package is byte-identical tov0.7.0. NoNEWS entry, no version bump, no tag — there is nothing a user could observe.
After merge
The first deploy with
clean: trueremoves the published copies. Worth confirminghttps://www.newgraphenvironment.com/fly/CLAUDE.htmlreturns 404 afterwards, and thatsearch.jsonno longer carries the text. De-indexing is a separate Search Consolerequest, and only after the URL 404s — a
robots.txtblock first would stop crawlersseeing the 404 and keep stale entries alive longer than doing nothing.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GBKqedyBysV7hB4DuL98ZR