The checker covered the identity strings a host sees, but not two other
kinds of template residue.
Descriptions: `Cargo.toml`'s `description` and `CLAP_DESCRIPTION` both
shipped as placeholders, and the latter surfaces in some host browsers.
Type names: `MyPlugin` and `PluginParams` appear at nine sites between
the struct definitions, the three trait impls, the two export macros and
the params field. Missing one is a compile error rather than silent drift,
so this is a convenience rather than a safety net - but naming the site
beats reading rustc, and the `Vst3Plugin` impl sits behind a cfg, so a
default build never reports it at all. Renaming the types is optional:
leave the canonical values alone and the checks stay green.
TODO markers are reported at a new informational level that never counts
toward pass/fail. They are legitimate while building, so failing on them
would just train you to ignore the run - but a finished plugin should not
quietly ship with five of them.
Two bugs surfaced while testing this:
- The stale scan drew its file list from `git ls-files`, which returns
nothing outside a checkout. In an exported tarball every stale spelling
was therefore reported absent - the section passed hardest exactly when
it could see least. It now falls back to `find` and says which source
it used.
- `grep -c` prints 0 and exits 1 when there are no matches, so the TODO
count's `|| echo 0` appended a second zero and produced an unparseable
value once a rename removed the last TODO.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
Make the template actually usable: it did not compile, its build docs
described output paths the bundler never produces, and its declared
licence conflicted with the artifact its default build emitted.
Also brings over codename-206's branding checker, extended so that
renaming — the first thing anyone does with a template — verifies itself.
Cargo.toml claims the pinned nih-plug rev makes builds reproducible, but
the rev only pins that one tree. Everything beneath it — roughly 200
crates.io dependencies — still resolved to whatever was newest at build
time, so two clones a week apart could compile different code.
A cdylib plugin is a final artifact rather than a library others depend
on, so the usual argument for leaving the lockfile out does not apply.
Checking it in also means CI builds exactly what was built locally.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
Adapted from musiikki/codename-206, where the same script guards a single
plugin. It matters more here: renaming is the first thing anyone does with
a template, and identity is duplicated across Cargo.toml, bundler.toml,
src/lib.rs and the docs with nothing keeping them in agreement. A
half-finished rename builds a bundle under one name while the docs and
deploy script look for another.
Changes from the codename-206 original:
- Checks for files this repo does not ship (deploy.ps1) report as `skip`
rather than FAIL, and start enforcing once the file appears.
- STALE ships pre-seeded with this template's own placeholders, and each
entry is skipped while it is still part of a canonical value. That makes
renaming self-checking: entries arm themselves as you change the value
they belong to, with no bookkeeping. Substring rather than equality,
because placeholders nest — "your-domain" lives inside the canonical
CLAP_ID.
- Covers identity this template carries that codename-206's did not:
VENDOR, EMAIL, the Cargo homepage feeding Plugin::URL, that the `env!`
indirection is still in place, and the README's bundle command.
- Asserts CLAP_ID is reverse-DNS safe. A stray capital or space there is
not a compile error, it is a silent identity change between machines.
Two placeholder spellings had to move to avoid false positives, since a
checker that cries wolf trains you to ignore it: bundler.toml's comment
contained the literal "Plugin Name", and the CLAP_ID TODO spelled the
placeholder a third way that matched neither the constant nor the STALE
list. The TODO now illustrates the form instead of repeating a placeholder.
.gitattributes pins *.sh to LF so core.autocrlf=true on Windows cannot
check the script out with a CRLF shebang that dash rejects.
Verified three ways: clean template passes, a partial rename names every
drifted file, and a complete rename passes and still compiles.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
Cargo.toml declared Apache-2.0 while the default build linked vst3-sys,
whose bindings are GPLv3 — so the artifact a fresh clone produced could
not legally be distributed under the licence the repo claimed. A template
should not hand that trap to every project cloned from it.
NIH-plug already anticipates this: its `vst3` feature exists, in upstream's
own words, "mostly for GPL-compliance reasons". Taking `default-features =
false` and re-exporting `vst3` as a local opt-in makes the default build
CLAP-only and genuinely Apache-2.0 clean, while `--features vst3` remains
one flag away for anyone who accepts GPLv3.
Gating the feature rather than relicensing keeps the decision with whoever
uses the template. Reversing it is a one-line default plus removing two
cfg attributes, where relicensing to GPLv3 now would commit every
downstream plugin to that choice.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
Two things in the build instructions were wrong rather than merely thin.
The stated output paths (`nih_plug_template.vst3`) are not what the
bundler produces: `cargo xtask bundle` names artifacts from the `name` key
in bundler.toml, so a fresh clone actually yields `Plugin Name.vst3`.
Anyone following the deploy steps verbatim would copy a path that does not
exist. The VST3 artifact is also a directory bundle, so the `cp` needed
`-r` to work at all.
The egui editor pulls in baseview and x11, whose build scripts abort on a
missing `gl.pc` with a pkg-config error that says nothing about this
project. Naming the packages up front turns a confusing first-build
failure into a one-line fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
The scaffold did not compile. src/lib.rs was written against an API that
does not exist at the pinned rev (f36931f):
- `impl Params` declared six methods that are not trait members
(`as_slice`, `param_by_id`, `param_by_name` and their `_mut` pairs) over
two types that appear nowhere in the crate (`AtomicParam`, `ParamId`).
The real trait is `unsafe trait Params` with one required method,
`param_map`, and is meant to be derived rather than hand-written.
- `type SysSysexMessage` misspells the associated type `SysExMessage`.
- `ChannelSamples` has no immutable `get`, so the passthrough loop's
`*frame.get_mut(ch).unwrap() = *frame.get(ch).unwrap()` could not resolve.
Replaced with `#[derive(Params)]` over an example gain parameter, so the
skeleton demonstrates the smoother and dB formatters a real plugin needs
instead of leaving an empty struct that teaches nothing. The passthrough
loop becomes an actual gain application for the same reason.
`editor()` is wired to a real `create_egui_editor` window in the same
change rather than a follow-up: nih_plug_egui was already a declared
dependency that nothing used, and the persisted `editor_state` field the
editor needs is part of the same params struct being rewritten here.
Splitting them would leave a commit whose params struct exists only to
serve an editor that is not there yet.
Verified against a local checkout of nih-plug at the pinned rev rather
than from memory, and confirmed by building.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn