Rules for any AI agent (Claude Code, Copilot, Cursor, Codex) working in this repository. CLAUDE.md and .github/copilot-instructions.md only point here.
The NuGet package RandomNameGeneratorLibrary (namespace RandomNameGeneratorLibrary): random person and place names from US Census lists and real star names, with no dependencies. About 3.9 million downloads, almost all of 1.2.2 (2016); 2.x since 2026-09-25. Library in RandomNameGeneratorLibrary/, unit tests in RandomNameGeneratorUnitTests/, the golden contract in tests/Golden/ and its replay in tests/RandomNameGeneratorLibrary.GoldenTests/, build-time tools in tools/. Start with ai-docs/HANDOFF.md; the current plan is the newest file in ai-docs/plans/.
- The same names for the same seed. A caller who seeds a generator (
new PersonNameGenerator(42),new PlaceNameGenerator(new Random(42)),new Random(42).GenerateRandomPlaceName()) gets exactly the names the published 2.2.0 gave, on .NET Framework and on .NET, and every other recorded answer stays the same. The one ruled exception is the place list, rebuilt in 2.3.0 (plan D3): the 35 place-derived cases per runtime are pinned intests/RandomNameGeneratorLibrary.GoldenTests/Exceptions/, and only cases whose name says they involve places may be there. The proof istests/Golden/2.2.0.*.json, recorded from the nuget.org package bytests/Golden/Captureand replayed by the golden tests;tests/Golden/PublicApi-2.2.0.txtguards names and parameter names, package validation guards the API shape. Never edit or regenerate a recording. A fix that would change a recorded answer (a name list, the order ofRandomcalls, an exception) goes under a new name with a decision entry inai-docs/decisions/and a changelog line; the old name stays exact until the next major. The embedded name lists are data: changing a line changes seeded output. - Targets. The library multi-targets
netstandard2.0andnet10.0: no net5+ APIs (ArgumentNullException.ThrowIfNull,HashCode) without an#ifor a polyfill. The net10.0 build is trim and AOT compatible. - Tests cover every artifact. Golden, unit, package validation at pack time, the consumers in
tests/consumers/run.sh(packed package in CI, nuget.org inverify-published.yml). A behaviour change lands with its test. Tests never touch the network. - Nothing reaches nuget.org without the maintainer. No API key is stored anywhere;
release.ymlpublishes through Trusted Publishing from a job that waits at thenugetenvironment for the maintainer's approval. Never push a package from a machine. - Releases follow one ritual. Update
CHANGELOG.md(a release heading carries its date), set<Version>inRandomNameGeneratorLibrary/RandomNameGeneratorLibrary.csproj, merge, wait forcito be green onmaster, then tagv<version>and push the tag.release.ymlbuilds, tests, packs, attests, waits for the approval, pushes and creates the GitHub Release. Then runverify-publishedwith the version, and bring the Unity package along:python tools/UnityPackage/sync.py <version>, merge, tag the merge commitupm/<version>and push that tag (OpenUPM builds from it;release.ymlignores it). Tag only after green: a tag on a failing commit burns the version number, because tags are not force-pushed here. - Dependencies. The library has none; keep it that way. Lock files are committed (
RestorePackagesWithLockFile); run plaindotnet restoreafter changing a PackageReference and commit the lock file; CI restores with--locked-mode. Dependabot opens weekly pull requests (nuget, dotnet-sdk, github-actions); merge whenciis green. Actions are pinned to commit SHAs; keep it that way. New package versions wait seven days before Dependabot proposes them, and three days before an agent installs them by hand. - Research beats recall. SDK, package and action versions change; re-verify any version older than three months.
- Document for handoff. Anything learned, decided or built goes into
ai-docs/before you finish; rewriteai-docs/HANDOFF.mdwhen work is left unfinished. - No AI attribution anywhere.
- Line endings. Files are LF (
.gitattributesand.editorconfig), sodotnet formatagrees on every OS; the*.strippedresources and the golden files must stay byte for byte as they are.
dotnet restore --locked-mode
dotnet format --verify-no-changes
dotnet build -c Release
dotnet test -c Release # net10.0 and net48 (net48 executes only on Windows)
dotnet restore -p:AuditPipeline=true --force # fails on any NuGetAudit finding, as CI does
dotnet pack RandomNameGeneratorLibrary -c Release -o artifacts # package validation against PackageValidationBaselineVersion
bash tests/consumers/run.sh VERSION artifacts # fresh consumers of the packed package (VERSION as in the csproj)
RandomNameGeneratorLibrary/Resources.*.strippedare the embedded lists (LogicalNameRandomNameGeneratorLibrary.Resources.<file>).tools/CensusToolsandtools/StarLists/build_star_lists.pyrebuild them; a rebuild changes seeded output and is a golden-contract decision, not a chore.tests/Golden/2.2.0.net48-windows.jsonand2.2.0.net10.0-windows.jsonwere captured from the published 2.2.0 by the program intests/Golden/Capture(whose emptyDirectory.Build.*files keep this repository's MSBuild settings out). Seeded names are the same on both runtimes; exception messages, the defaultRandomandFileCompressor's bytes differ, so each runtime has its own recording.- A locked-mode lock file for a multi-OS matrix must not depend on anything the SDK infers per OS:
Microsoft.NETFramework.ReferenceAssembliesis referenced explicitly withPrivateAssets="all", and the net48 test exes pin a RuntimeIdentifier withSelfContained false: win-x86 for the unit tests, win-x64 for the golden replay, because the net48 recording came from a 64-bit process and one OutOfMemoryException message differs in a 32-bit one. - The Visual Studio
.gitignoreignores everylog/folder; everlast'sai-docs/log.mdis a file and is unaffected. - The publish job has no checkout, so it pins
dotnet-versioninstead of readingglobal.json. - After a release, set
PackageValidationBaselineVersionto the released version. Packages/com.m4bwav.random-name-generatoris the Unity package (OpenUPM). ItsRuntime/DLL and XML are copied from the nuget.org package bytools/UnityPackage/sync.py, never built here, and.gitattributeskeeps them byte for byte;sync.py --checkproves it. Unity can't compile the source because the name lists are embedded resources. Keep the.metafiles: Unity ignores files without one in a registry package. OpenUPM (data/packages/com.m4bwav.random-name-generator.ymlin openupm/openupm) usesgitTagPrefix: upm/, so thev*NuGet tags never reach it. Web (WebGL) builds drop embedded resources unlessPlayerSettings.WebGL.useEmbeddedResourcesis on;Editor/WebGLEmbeddedResourcesCheck.cslogs an error when it is off. Details:ai-docs/notes/2026-10-11-openupm-unity-package.md.
ai-docs/INDEX.mdlists what past sessions learned here (solutions with verified commands, decisions with reasons, plans). At the start of a task, scan it and open only the entries whose title or tags match; no line matches:everlast.py search "<key terms>"before concluding nothing was recorded. Readai-docs/HANDOFF.mdwhen continuing unfinished work (everlast-resume skill).- Before acting on an entry marked
(recheck due), runeverlast.py recheck <entry>, re-run its Verified-by command only when that is read-only or safe (a build, a test, a version query), then recordeverlast.py verify <entry>orverify <entry> --failed "what broke"; a fix that changed is superseded, never reused blindly. - Before finishing a task that hit a dead end, verified a non-obvious command, made a design choice, or taught you something about the user, record it (everlast-capture skill, or
everlast.py note/handoff); rewriteHANDOFF.mdwhen work is left unfinished. Say "nothing to record" when that is true. - Anything naming a person, an internal host or name, a credential, or an opinion about people goes to the private sidecar (
--private), never here. Lessons about the user or this machine go to the user tier (--user). - Rules go in this file, system layout in CODEMAP.md; the doc set holds only what could not be re-derived from the code in a minute.
- Link documents together with relative markdown links: every markdown folder is reachable from an index whose lines say when to read each file (
ai-docs/INDEX.mdis generated from frontmatter; give entries a one-linesummary), and an entry links the entries it relates to on a typedRelated:line (supersedes,contradicts,builds on,see also). The set then reads as a graph for people in Obsidian and for agents alike. No wikilinks in the repo.