The site generators move out of lichen-core into their own crate, lichen-ssg, below core.
Generators hold their settings. A generator is built holding its BuildSettings, and its methods take no settings. BuildSettings holds the site's four folders, the strict-scripts rule and the shared template slot; the fields moved out of Settings, which reads through to them and builds the generator again over new folders in with_overrides. The two kind-level constants (a new site's settings file, standard.site's front matter names) are SSGKind's. Hugo finds its binary where it runs it, so no constructor does I/O.
lichen-ssg. It holds lichen's own generator, Hugo, mdBook and a site's own script, with BuildSettings, the templates and the starter files. Core depends on it and re-exports it as ssg. It names no lichen-core, lichen-site, lichen-server or add-on crate; a structure test (lichen_ssg_sits_below_core) holds that. The hugo and mdbook features and their dependencies moved with it, and core's forward to them. A debug site process's read grant names the starter files' new path, ../lichen-ssg/static/.
Items made pub. The generator and template items core calls across the new boundary are pub; three more are behind test-support. The raw-write helpers stay private to lichen-ssg.
A reset to the hub can be given its limit (another phase's change, below the break). The rewrite's stays RESET_FOR, five minutes.
SSG-CRATE + CI-DOCKER-IN-JOBS, the whole phase in one reading PR. Plan: https://tangled.org/notplants-bot.bsky.social/lichen-orchestrator/blob/main/machine-docs/plans/SSG-CRATE-2026-11-04.md
Only the phase's Rust product code is here: every .rs file under crates/<crate>/src/ (no tests folder in its path), which is what e2e/ci/count_product_code_lines.py counts as product. The head is the base plus one commit that sets each of those files to its content at the end tip (added, changed, deleted and renamed files alike). Tests, docs, scripts and non-Rust files (including Cargo.toml and Cargo.lock) are not in it, so the crates here are not meant to build on their own. Reading copy; nothing here is to be merged.
Source PRs on lichen.page.review (product), in order #
- #3779 the reshape in core:
BuildSettings, the generator trait without settings (SSG-CRATE unit 1) - #3783 the move into
lichen-ssg, its prep in core (2a) and the crate (2b) (unit 2) - #3785 the close: the structure test and the feature checks (unit 3)
- #3786 the repair the first closing standard asked for
Product commits from other work on stack, in this range #
- TEST-AND-CI-HEALTH unit 3, #3781: a reset to the hub can be given its limit (
c13030ba0). It is the description's last paragraph, not SSG-CRATE's. - CI-DOCKER-IN-JOBS (
CI-DOCKER-IN-JOBS-2026-10-19.md) ran at the same time and ended at the same tip. Its work is CI, Nix and test files; it changes no counter-product Rust file, so nothing in this PR is its own.
Base, end tip, count #
Base 65a3cb4881ed2011eea4079368510f1714a63a8b. End tip 5328aaf2a8525af4585a8fa632a014da131dc3c5. Head 0358be2f071496eb9a5e380cf88321d012deb5e3 (41 files: 33 added or changed, 8 deleted).
Counter, base to end tip (the phase as lichen.page.review has it):
count-product-code-lines 65a3cb4881ed..5328aaf2a852: NET +508 · counted +2596 -2551 · moved out of the counted set +463 · comment-only deletions flagged 3 · macros flagged 0 · tests-folder code flagged 0
Counter, base to head (this PR):
count-product-code-lines 65a3cb4881ed..0358be2f0714: NET +25 · counted +2576 -2551 · moved out of the counted set +0 · comment-only deletions flagged 3 · macros flagged 0 · tests-folder code flagged 0
The two are equal in every Rust line. They differ only by lines outside the Rust files that the counter reads from the end tip: lichen-ssg's build.rs and the starter sites' HTML and CSS as moved out of the counted set (+463), and the included lichen-ssg/src/templates/layout.html (+20). Those files are not in this PR.
Check #
git diff base head lists only counter-product Rust files, and the head's content equals the end tip's for each of them. For the counter, the head's counted and NET lines equal the end tip's once those non-Rust lines are taken out.
Record #
SSG-CRATE is COMPLETE at stack 5328aaf2a8525af4585a8fa632a014da131dc3c5 (coordination report machine-docs/reports/SSG-CRATE-2026-11-10.md), and CI-DOCKER-IN-JOBS (plan machine-docs/plans/CI-DOCKER-IN-JOBS-2026-10-19.md) is COMPLETE at the same tip, which is why the two are one PR: either alone would include the other's commits.