Devlog · July 2026 · nlvm.dev
The site that builds itself
This site used to be hand-written HTML living inside the nlvm repo's docs/ folder — five pages, one shared nav, and every change meaning five edits kept in sync by hand. nlvm.dev is now its own repository, and the pages themselves are produced by an NL program. Two separate decisions led here, and it's worth writing down why.
Four repos, one organization
By July 2026 the project was nlvm-specs, nlvm, nlvm-demos, and this site — four repositories, all under a personal account, with more ideas already queued. That's the point where "personal repo" stops being a convenience and starts being a source of confusion: no shared identity, no natural place for the next one. The fix was to create the nlvm-lang GitHub organization, transfer all four projects into it, and point nlvm.dev and specs.nlvm.dev at their GitHub Pages. The domain and the dedicated site repo followed naturally once the org existed — they weren't the starting motivation, just the obvious next step.
Why generate the site in NL
The immediate reason was maintenance: hand-duplicated HTML across pages, with a shared nav and footer copy-pasted five times, was already becoming impossible to keep consistent. Any templating approach would have fixed that — PHP or Python would have worked fine. The choice of NL was deliberate: the toolchain was far enough along to build real tooling with it, and a static site generator is exactly the kind of ordinary, unglamorous program that tells you whether a language is usable outside its own test suite. This is the same instinct behind the toolchain releasing itself via tools/Release.nl — dogfooding isn't a slogan here, it's a second source of bug reports.
How Build.nl works
src/nl/Build.nl is about 235 lines, plus two small data classes, PageSpec and NavItem. There's no framework: a page is a content fragment in src/pages/ and a PageSpec entry (title, description, paths, which nav item is active); src/layout.html is the one shared shell, and generation is a sequence of {{PLACEHOLDER}} string replacements — {{NAV}}, {{CONTENT}}, {{TITLE}}, and so on:
string pageOut = layout;
pageOut = pageOut.replace("{{TITLE}}", page.title);
pageOut = pageOut.replace("{{NAV}}", nav);
pageOut = pageOut.replace("{{CONTENT}}", content);
system.io.File.writeAllText(page.outPath, pageOut);
Root-relative links are the one place this needed real logic rather than string substitution: devlog pages live one directory deeper than the rest, so every PageSpec carries a root prefix, and the nav renderer special-cases the active devlog link so that a devlog post links to index.html instead of back to itself through ../devlog/index.html.
The more interesting part is that some placeholders aren't static text — they come from actually running the toolchain. The build step shells out to the sibling nlvm checkout's release binaries via system.ps.Process, runs the real nltest conformance suite, and regex-extracts the pass count from its own stdout:
string testStdout = Build.runAndGetStdout(
new string[]{"target/release/nltest", "tests"});
string testSummary = Build.lastLine(testStdout);
auto countMatch = system.text.Regex.matchFirst("(\\d+) total", testSummary);
This page you're reading goes through the same substitution pass as every other one: the test count quoted on the initial-build post is filled in from that live regex match, not typed in by hand. The version banner and the Release.nl source quoted on the getting-started page come from the same run. If the numbers on the site are wrong, it's because the toolchain gave the wrong answer — not because someone forgot to update a page.
What dogfooding actually found
Small, but real: GitHub's Linguist doesn't know NL and was mislabeling every .nl file as NewLisp in the repo's language stats, fixed with a one-line .gitattributes. Not a compiler bug, but exactly the kind of friction that only shows up once a language has to coexist with the rest of the ecosystem's tooling instead of just its own test suite — which was the actual argument for building the site this way in the first place.
Source is in nlvm.dev, mainly src/nl/Build.nl. The four repositories now live under the nlvm-lang organization.