Prior PR, still open and under discussion:
Very much this. Itâs like walking into a room waving a big gun around and wondering why everyone else pulled their gun on you.
If anybody would like to DM me, Iâd like to put together a working group on the Agents thing. Iâm bummed that work in this area wasnât handled as part of GSoC but I think weâre well at the point where a polished approach could be helpful.
DM me here on Discourse and letâs put together a team.
I am against the symlink. Any decent agent must support the standard locations.
Well thatâs worse:) Itâs much easier to help newcomers when their slaup is obvious and quickly rejected?
@lucasew please do!
(PS: Iâm not quite active here, but this thread got my attention.)
Nix needs proper AI skills, written by the community in a shared effort. Itâd be so good for developers starting with Nix, as well as experienced ones using AI to automate tasks.
I see that thereâs a lot of discussions between people, some who seems to not love AI so much, and I get it â but for those using AI to get real work done, itâd be awesome!
A model-agnostic AI skill for Nix would sure reduce errors from AI output over Nix tasks. Itâs pure prompt engineering, loaded right at modelâs first moments processing a user/agent prompt, and since itâs purpose-specific, it could be customized for AIs.
LLMs can read the manual, and are good at that.
I saw this argument. Itâs solid, makes sense â but then, an AI would have to fire up web searches, HTTP requests, etc., just to figure out the very first steps (whatâs Nix, what problem it solves, how does it work overall, its most important terminology, where to find official/accurate info like official docs and forums, official news, etc.) â which all turns into token [and context] consumption. If thereâs a skill providing the basic building blocks of using/working with Nix, token usage would be optimized, and of course the time taken to get the final results would be seriously reduced.
I know it does not solve AI slopness, it wonât make AI perfect, etc., etc. â but it sure does help AI on getting closer to do things right.
Yes, but doing skills for everything would create redundancy and I donât want to maintain that, do you? Ideally the skill/agents should be about telling where to look for information and guidelines. Perhaps a common denominator that both human docs and agents can consume in a efficient way. Perhaps even as part of CONTRIBUTING.
I am having a good time with that AGENTS, stuff where the model struggles is mostly lazyness and skimmimg that the model can do or it assuming it has a decision autonomy it doesnât have. Nothing that a short leash and a âdonât ping people if I didnât ask you toâ policy canât do.
In my experience, today models have a pretty good understanding out of the box about Nix. They just mess up on the guidelines without guidance. Like nixpkgs is a project that is a evolving standard, with processes and stuff, AI today already can handle that. Doing skills for every facet I think is overkill. It can figure out through human documentation, we just have to set up a basic checklist and guideline for it to give less review hassle for us. Like reviewing by asking a predefined set of questions.
For Nix itself, I think the most impactful trick to teach it is to do Nix introspection, like, instead of it fetching the source at specific checkout in TMP to then inspect the upstream code, it could just nix build .#package.src and inspect that.
Additionally, I am working on a janitor skill. A generic thing that I think I will keep as a proprietary thing that is a process to find tech debt issues generically on repos by supporting on best practices skills and guidelines. I asked it to give me a unslopified report of the 10 most impactful issues of nixpkgs. I wasnât aware of the issues. This was a 5 minute work essentially.
Report
nixpkgs janitor report
Tree: 8f7639a0d4d3 on upstream/master (scanned 2026-07-05).
Walked the janitor checklist on lib/, pkgs/stdenv, pkgs/build-support, pkgs/by-name, pkgs/top-level, nixos/lib, nixos/modules (spot checks), ci/, contributor docs, and maintainer scripts. Ranked by how much they cost contributors and how far the blast spreads. Package-local TODOs in pkgs/by-name were ignored unless they point at shared machinery.
Checked and set aside:
- Vendor best-practices skills: none in the local corpus cover Nix/nixpkgs, so no âapply skill Xâ items.
- AI entrypoints: no
AGENTS.md,CLAUDE.md, or.github/copilot-instructions.md. Nothing to deslop there. setup.shlogging: already levelled viaNIX_DEBUG/NIX_LOG_FD. Noisyechoremains in failure paths; not top ten.- Impure
getEnv/currentSystem: intentional inimpure.nixand eval entrypoints.
Numbers are approximate.
1. CI turns off whole nixf rule families
Explanation
ci/treefmt.nix runs nixf-diagnose --auto-fix on *.nix, then nixfmt at a higher priority âto clean up afterâ it. That pairing admits the tools fight. On top of that, four unused-binding rules and four prelude/primop rules are --ignored, with TODOs that say âremove and fixâ or point at open nixd issues (#761, #762). Generated Home Assistant and hackage2nix files are excluded instead of fixing the generators. ci/nixpkgs-vet.nix still carries in-tree checks marked for upstreaming (nixpkgs-vet#164, #166) and uses grep ... || true so a find failure looks like success.
At this repo size, disabled static checks mean dead formals and lets only die in review.
No change to package build output; CI starts failing on code it currently skips.
Plan of attack
Pick one ignored rule (unused let in lib/ and pkgs/build-support/ first). Enable it on a path allowlist, fix in batches, widen the list, delete the flag. Put calendar expiry comments on ignores that wait on nixd.
Upstream the <nixpkgs/ and case-conflict checks into nixpkgs-vet and drop the shell copies. Make generators emit nixfmt/yamlfmt-clean output so excludes can go. Keep nixf priority below nixfmt until auto-fix stops fighting the formatter, or drop auto-fix for rules that churn formatting.
2. Bot fallback code past its delete-by dates
Explanation
ci/github-script/bot.js still fetches pr.yml artifacts with âRemove this after 2026-02-01â. It still rebuilds package lists from maintainers.json when packages.json is missing, with âshould be gone ~ February 2026â. It still passes advanced_search: true with âRemove after 2025-11-04, when it becomes the default.â Today is past all three. Extra branches mean extra API calls, harder reviews, and labels derived from the wrong artifact shape on old PRs.
merge.js still plans a more efficient touched-file download. reviewers.js and check-target-branch.js note structural overlap with prepare.js.
Behavior change for very old open PRs that only have expired artifacts; fine for current traffic.
Plan of attack
Delete the pr.yml fallback and the maintainers.json package-list fallback; run the bot test suite / a dry run on a recent PR. Drop the redundant advanced_search flag if the API default holds. File or finish the by-name path-based âchanged packagesâ work so eval artifact expiry hurts less. Fold target-branch checks into prepare.js in one pass. Re-read every TODO: Remove after in ci/github-script/ on each release branch cut.
3. pkgs/by-name cannot express custom callPackage args
Explanation
~21.4k package.nix files live under pkgs/by-name. all-packages.nix is still ~11.2k lines with ~1.5k callPackage mentions. Examples still bind by-name paths with extra args (alacritty + withGraphics, ollama-* accelerators, etc.). by-name-overlay.nix keeps _internalCallByNamePackageFile and a TODO: until by-name owns custom call args, all-packages.nix must override and CI must detect empty {} overrides.
Only by-name PRs auto-merge. The README still points at automated migration (nixpkgs-vet#56) that cannot finish while this hatch exists. The two-letter shard prefix is documented magic; the real pain is the split brain between overlay and all-packages.nix.
Moving implicit args into the package function changes .override interfaces. Release-note those.
Plan of attack
Inventory non-empty second arguments and other by-name shadows. Split into renamed dependency parameters, real defaults, and alias/override wrappers.
Add a CI-enforced way to declare call defaults inside the package directory. When the inventory is covered, make stage.nix error on all-packages.nix shadows and delete _internalCallByNamePackageFile. Run the empty-{} migrator for the rest; keep renames in aliases.nix. Gate on ./ci/nixpkgs-vet.sh and ci/eval.
4. Language package sets never got a by-name layout
Explanation
perl-packages.nix ~40k lines, python-packages.nix ~23k lines, ~9.7k directories under pkgs/development/python-modules. by-name docs exclude python3Packages.callPackage and nested attrs. Edits collide on rebase; auto-merge does not apply; eval/review cost follows set size. Haskell already generates lists and then excludes them from yamlfmt because the generator emits churn.
Python still carries overrideStdenvCompat and __stdenvPythonCompat* hooks with removal TODOs in python-packages-base.nix and mk-python-derivation.nix, so even the builder API is mid-migration.
Overlay and withPackages behaviour must stay stable; diff eval output.
Plan of attack
Pathfind on Python. Write invariants (overlays, toPythonModule, disabledIf, cross, stubs, overridePythonAttrs). Add a sharded tree plus an overlay map with the same performance budget as by-name-overlay.nix. Reuse vet ideas: one package per dir, no out-of-tree refs, no dup attrs.
Migrate in chunks with eval diffs; shrink python-packages.nix to a shim. Finish overrideStdenvCompat removal on a named release so the new layout does not preserve the shim. Fix hackage2nix/output generators before touching Perl by hand. Update doc/languages-frameworks/ so new modules never join the monolith.
5. mkDerivation teaches two dependency languages
Explanation
make-derivation.nix marks nativeBuildInputs / buildInputs (and propagated variants) as legacy next to depsBuildHost and friends, but both work. strictDeps, configurePlatforms, and doCheck / doInstallCheck defaults still depend on platform and config flags, with TODOs citing #87909, #178468, #33599. stdenv/generic/default.nix leaves fixes commented to avoid mass rebuilds. Manual chapters still lean on the legacy names (doc/stdenv/stdenv.chapter.md alone has dozens of nativeBuildInputs hits).
Same concept, two vocabularies. Cross reviews stay harder than the offset model deserves.
Flipping defaults changes every derivation on staging.
Plan of attack
One design note: each conditional default, end state, rebuild set. Flip config.strictDepsByDefault, configurePlatformsByDefault, and check defaults one at a time on staging. Warn via problems before hard errors.
Prefer offset names in new docs and examples; keep legacy attrs as aliases. Optionally vet new stdenv code in pkgs/build-support for legacy-only style where offsets are clearer. Use ci/eval compare for rebuild proof, not guesswork. pkgsi686Linux special-casing in stage.nix belongs in the same cleanup once it can be a variant.
6. meta validation is weaker than the comments admit
Explanation
check-meta.nix documents a bug: string licenses make isUnfree return false. License type is still attrs | str with âTODO disallow strâ. Maintainers and teams are listOf attrs instead of the types already prototyped in lib/tests/maintainer-module.nix. problems.nix has thinner docs/remediation than the framework needs.
~14.1k by-name packages omit mainProgram, so lib.getExe guesses. ~1.2k by-name packages still use mkDerivation rec while ~15.7k already use finalAttrs.
Invalid meta that evaluates today will warn, then error. String license policy fixes can change unfree detection for mislabeled packages (correctness fix, still user-visible).
Plan of attack
Fix the string-license isUnfree bug under tests. Warn on string licenses and freeform maintainer attrs for one release, error the next; codemod to lib.licenses.* and list entries from maintainer-list.nix.
Require mainProgram for new by-name packages that install a single obvious bin/ entry; report high reverse-dep misses for backlog PRs. Point remediation strings at real manual anchors. Codemod trivial rec to finalAttrs in by-name. Keep known-vulnerability handling fail-closed.
7. NixOS option shims and default types.str
Explanation
~1635 mkRemovedOptionModule / mkRenamedOptionModule / mkAliasOptionModule / mkChangedOptionModule uses. ~5571 types.str hits, ~458 files with bare type = types.str;. ~74 mdDoc / literalMD / mkAliasOptionModuleMD leftovers; mkAliasOptionModuleMD is warned for removal in 26.05.
Every config eval pays the rename tables. types.str accepts "" and path-like values, so mistakes show up when a service starts.
Removing aliases and narrowing types needs transition renames and release notes.
Plan of attack
Generate shim inventory with add version and delete version. On unstable, expire shims older than N releases (error, then remove) and auto-draft release notes from the list. Finish MD alias removal for 26.05.
Replace hot-path types.str with nonEmptyStr, singleLineStr, path, passwdEntry, or a submodule (users, networking, nginx, security first). CI nag on new bare types.str without a comment. Optional: thunk counts on nixos/release-small.nix before/after large deletions.
8. Contributor docs are split and partly hollow
Explanation
CONTRIBUTING.md ~965 lines, pkgs/README.md ~1264, pkgs/by-name/README.md ~219, plus doc/contributing/*. Several manual chapters are stubs that only link to GitHub (quick-start, contributing-to-documentation). Readers bounce between root, pkgs/, doc/, and nixos/README.md for the same questions (how to add a package, review norms, auto-merge rules).
That is human-facing duplication in the same spirit as AI entrypoint slop: copy instead of a single pointer map. It also hides the non-obvious rules (when all-packages.nix must stay, when inherit ({ ... }) is required for multi-version packages).
Doc-only; no eval change.
Plan of attack
Make root CONTRIBUTING.md a short map: links to pkgs/README.md, pkgs/by-name/README.md, nixos/README.md, doc/README.md, CI/eval README, modular services README. Move procedural detail once; delete repeats.
Replace stub chapters with in-tree relative links that render in the manual, not only master (Oleg Smirnov) ¡ GitHub URLs. Add a single âwhere does this package file go?â decision section that includes by-name limits and language sets. Keep release-specific process out of package authoring docs.
9. Image and channel builders repeat the same FIXME
Explanation
make-disk-image.nix, make-single-disk-zfs-image.nix, and make-multi-disk-zfs-image.nix all say merge with channel.nix / make-channel.nix. make-channel.nix hardcodes officialRelease = false with a makeSourceTarball FIXME. make-iso9660-image.nix keeps a Hydra copy workaround. Fix one flavor, miss another. Release and installer paths are high-stakes for a copy-paste bug.
Internal refactors are fine; warn if out-of-tree scripts import private nixos/lib paths.
Plan of attack
Extract one helper for channel closure, registration, and ownership. One attrset for release/version metadata with tests. Assert metadata parity across image kinds in installer tests. Re-validate the ISO copy trick on current Nix/Hydra and keep it in one place. Document public entry points in nixos/README.md. Delete duplicates after linux smoke tests.
10. Module system API is deprecated in place with gaps in errors
Explanation
lib/modules.nix (~2342 lines) still accepts deprecated evalModules args / check, warns on external lib.modules.*, and ships mkAliasOptionModuleMD toward 26.05 removal. FIXME: handle mkOrder? sits in ordering logic. Merge helpers have a âmerge into mergeOptionDeclsâ TODO. Error text sometimes omits useful opt context. nixos/lib/eval-config.nix mirrors extraArgs / check deprecations. nixos/lib/from-env.nix duplicates lib.maybeEnv (which itself lives under lib/deprecated/misc.nix while still re-exported from lib/default.nix) and is imported from nixos/default.nix, activation, and option-usages.
External frameworks and in-tree code share a blurry boundary. Weak errors turn ordinary misconfigs into long threads. maybeEnv sitting in lib/deprecated while NixOS reimplements it is inconsistent wording about what is supported.
Removing deprecated args/attrs breaks callers still on the old surface. from-env removal is straightforward if all callers switch to lib.maybeEnv.
Plan of attack
Publish the supported module-system surface in lib/README.md and the manual; stronger warnings in 26.05, removals aimed at 26.11. Migrate in-tree callers to config._module.args / config._module.check.
Fix mkOrder handling and richer invalid-option errors with tests in lib/tests/modules. Only merge declaration helpers if NixOS eval thunk counts do not regress. Remove mkAliasOptionModuleMD on schedule. Switch from-env.nix callers to lib.maybeEnv and delete the file; if maybeEnv remains supported, move it out of lib/deprecated or document why it stays. Keep a short lib changelog for home-manager and friends.
Honorable mentions
cc-wrapper (~1015 lines) plus setup hooks. Critical path, arch exceptions, env vars that need stdenv rebuilds to rename. Refactor behind tests on Darwin/musl/static/cross; schedule renames on staging.
Full ci/eval wants ~16GB RAM (chunkSize default 5000 is unexplained magic in ci/eval/default.nix). Name the default, document laptop quickTest / remote eval paths, tune chunks with measured memory curves.
maintainers/maintainer-list.nix (~31.5k lines). Shard or generate; keep team-list.nix stable.
Modular services. lib/services/* is five files and ~11 _class = "service" modules beside ~1700 nixos/modules/services files. README still says âTBD: describe best practicesâ and points at #428084. Write the missing practices and an exit criterion before growing dual maintenance.
Splicing shortcuts in splice.nix / stage.nix (partial splice for performance, pkgsi686Linux TODO). Needs benchmarks before uniformity.
Order to chip at this
- #2 delete expired bot branches (small, overdue).
- #1 enable one nixf rule on a narrow path.
- #6 fix string-license
isUnfreebug + start maintainer typing warnings. - #8 shrink CONTRIBUTING to a map (clarifies everything else).
- #3 design by-name custom call args.
- #4 Python sharding design after #3âs vet patterns exist.
- #7 shim expiry policy + hot-path
types.str. - #10 module API + delete
from-env.nix. - #5 / cc-wrapper on staging windows.
- #9 when already touching image code.
- Modular services docs/exit criteria before more ports.
Counts
| Thing | ~Value |
|---|---|
TODO/FIXME/HACK/XXX in pkgs/build-support |
96 |
same in lib/ |
52 |
same in pkgs/top-level |
45 |
same in pkgs/stdenv |
35 |
lib.warn / deprecate / remove throws in core libs |
1611 |
pkgs/by-name/**/package.nix |
21416 |
all-packages.nix lines |
11212 |
callPackage mentions there |
1552 |
perl-packages.nix lines |
39777 |
python-packages.nix lines |
22626 |
| Python module dirs | 9755 |
| NixOS compat shim calls | 1635 |
types.str in nixos/modules |
5571 |
files with type = types.str; |
458 |
| mdDoc-era leftovers | 74 |
by-name without mainProgram |
14123 |
by-name with finalAttrs |
15746 |
by-name with mkDerivation rec |
1232 |
mkDerivation rec across pkgs/ |
2012 |
_class = "service" modules |
11 |
nixos/modules/services nix files |
1708 |
lib/modules.nix lines |
2342 |
setup.sh lines |
1834 |
cc-wrapper/default.nix lines |
1015 |
maintainer-list.nix lines |
31560 |
CONTRIBUTING.md lines |
965 |
pkgs/README.md lines |
1264 |
So now weâre trying to hide the fact that an LLM is doing the work for you?
I never hid
I read what it gives to me, ask questions when I have them, ask it to reformulate, to take more stuff into consideration, sometimes I ask it to work on a specific situation and if it makes sense I send a draft. When I think itâs ready for review I undraft the stuff.
That Janitor skill runs overnight though Jules using another trick I have because Jules by itself doesnât support skills natively. And itâs getting really good at finding duplicated logic around a codebase. Lots of situations that would be overlooked or be too much hassle for too little benefit being slashed in the background.
If the pass is not so great then I iterate on the questions it has to answer so on the PR body I have the whole context.
I think thereâs a lot of possibility for Nix + LLM: We have a huge amount of maintenance that runs every release cycle that is tedious for a human, but not something a conventional automated system can manage right away. Iâve been coming at this from a different direction: piecewise AGENTS.md files for individual nix flakes/the general ecosystem.
The skill is a good place to start! Itâs a tiny addition, and it has the possibility to ease adoption and improve feature discovery, both of which are huge pain points.
We should talk in DMs!
Came across this in a GitHub search for nixpkgs + LLM: GitHub - jpds/temporal-nixpkgs-workflows ¡ GitHub
Most people already know this one, but for those who might not:
Has most of the popular harnesses all prepped for nix in one place.
Made with Astro, DaisyUI and the impeccable skill because I am not a designer.
Do you know who wrote impeccable? Paul Bakaus, who I worked with for many years and count as a friend. He could only write that because he spent decades honing his skills including deep expertise as a Developer Advocate at Google.
Impeccable does amazing work, and it puts human judgement front and center. You still need to review and suggest corrections, and that requires taste and experience.
If you want to write agent skills for nixpkgs, you need to put in your time as a committer (with recent experience) and reviewer. Learn whatâs important within the community. Make your skills small, composable, assistive, and outside the codebase where developers can choose to use them. Augment individual tasks, with a person being in charge.
Human in the loop, always.
P.S. Please read Unwritten style guides should be written ¡ Issue #387072 ¡ NixOS/nixpkgs ¡ GitHub (I linked to a specific comment s a starting point).
A automation around validating standardization end to end to make nitpicking 100% pointless would be the endgame. As I said, standardized formatting was a huge leap already. My dream is we only being responsible of debating technical merit and letting machines check the rest as determinisitically as possible. And AI being the tool to run the checklists so we can focus on the techincal merit of the stuff. Perhaps even delegating to AI the routinely boring tasks like dealing with simple build and update failures transforming a making problem into a reviewing problem. Instead of going through a queue of requests, going through a queue of suggested solutions well fundamented on tests and good tested hypothesis. Heck, Grok is being BEAUTIFUL for this kind of work.
Therein lies the rub: AI is non-deterministic. Even if you set the temperature to 0, youâll get an inconsistent result when 1) the LLM updates, 2) somebody updates an AGENTS.md, etc.
Itâs also hugely inefficient and expensive (the foundation already has a huge Github bill, it doesnât need an AI bill thatâs orders of magnitude higher).
If you ever write a PRD (Product Requirements Document), the first rule is specify outcomes, not implementation. AI is an implementation detail and comes with enough ethical baggage that it needs to be opt-in (read: up to the individual maintainer). This also aligns with our current AI policy.
On requiring everyone to use AI:
Itâs like the poor lord who went around giving everybody liver pate for Winterfair and wondering why nobody ever returned the favor. - Lois McMaster Bujold, Winterfair Gifts
AI wouldnât be required. Itâs just a matter of how much time vs tokens you are willing to spend on details.
But there are facets of the guidelines, like formatting, that can be checked by deterministic processes. The less we have to do checklists manually the less time we waste.
AI would give a good v0, but over time we could check the most common issues using deterministic processes, like how nixpkgs-vet is doing.
Please read AI is burning out the people who keep open source alive (one of a great many examples)
Thatâs completely irrelevant to this discussion. If you have automation to contribute, then do so. AI is not automation.