crates/nornir-docgen/Cargo.toml
[package]
name = "nornir-docgen"
# 0.1.1 (2026-08-27): the `nornir:claim:` gate moved IN from `nornir/src/docs/`
# and now sits on this crate's own render/book entry points. That is a behaviour
# change for every consumer — a render can now return `Err` where 0.1.0 always
# returned `Ok` — so it gets a version, and consumers get a FLOOR of 0.1.1
# rather than `^0.1.0`. A `^0.1.0` requirement would let a registry build resolve
# the ungated 0.1.0, which is the same "the engine shipped, the door did not"
# shape the gate exists to close.
#
# 0.1.2 (2026-08-27): 0.1.1 shipped and could not be RESOLVED. It was uploaded,
# it is on the index, and a cold-cache `cargo generate-lockfile` against it dies
# on `bisync` — every one of that crate's seven published versions is yanked, and
# `gix 0.86` reaches it through `gix-protocol ^0.64.0`. The gate therefore did not
# arrive at a single registry consumer: the exact failure 0.1.1 was cut to
# prevent, one layer down. 0.1.2 is 0.1.1 with `gix` moved to 0.87 (see the dep
# line) and nothing else changed. Consumers already floor at `^0.1.1`, which
# admits 0.1.2, so no consumer manifest moves.
#
# The lesson, for the next release of anything here: a green `cargo publish
# --dry-run` is NOT evidence a crate can be consumed. The dry run verifies
# against the LOCAL cargo cache, which happily holds a yanked `bisync` it
# downloaded weeks ago. Only a virgin `CARGO_HOME` resolving the real index can
# see it.
version = "0.2.2"
# 2024: the lifted code uses let-chains (stable only on edition 2024, which is
# also what nornir itself builds with) — nine other edda leaves are 2024 already.
edition = "2024"
# GUARDRAIL: the warehouse-free doc-book + mashup ASSEMBLY half lifted verbatim
# out of nornir's `docs/` subsystem — the `nornir:gen:*` marker engine and every
# section renderer (sections), the managed-docs layout/render driver (layout),
# the whole-doc book assembler (book), the bench-status card (bench_status), the
# test inventory (test_inventory), the pre-commit hook installer (hooks), the
# Codeberg Pages stager (pages), the typst depgraph/bar-chart SVG (svg), the api
# extractor (apidoc) and the AI-context builder (ai_context). nornir keeps
# byte-stable `docs::*` re-export shims; the halves that read the Iceberg
# warehouse (docs::warehouse, docs::search, docs::release_book, the Docs gRPC
# service) stay behind in nornir — they are genuinely Mímir.
#
# It was `publish = false` "until the release cascade can carry it: it path-deps
# `nornir-docbook` (itself `publish = false`) … Publishing this crate is a
# follow-up that starts with publishing nornir-docbook." That follow-up ran on
# 2026-08-23: nornir-docbook is on crates.io, so the last unpublishable path dep
# is gone. Driven by nornir 0.5.5, whose own publish fails while either is
# missing from the index.
publish = true
description = "The warehouse-free documentation ASSEMBLY half of the nordisk constellation: the nornir:gen marker engine + section renderers (bench tables, mashup, coverage, depgraph), managed-docs layout, whole-doc book assembly, bench-status card, test inventory, pre-commit hooks, Codeberg Pages staging, typst SVG charts, API extraction and AI-context building. Lifted verbatim out of nornir's docs subsystem; nornir keeps byte-stable re-export shims and the Iceberg-warehouse halves stay behind."
license = "MIT OR Apache-2.0"
repository = "https://git.gunnar.rs/nordisk/edda"
keywords = ["documentation", "markdown", "generator", "benchmark"]
categories = ["development-tools"]
exclude = ["/.nornir/"]
[features]
# The typst-backed media/export half, mirroring the gating nornir's `docs-export`
# feature applied when these modules lived there: `export` lights `svg` (typst
# depgraph + bar-chart SVG), `book`/`apidoc`/`ai_context`, and the `export`
# re-export of nornir-docbook's render engine. OFF ⇒ the marker engine still
# works fully, with the pure inline-SVG text fallbacks (no typst in the build).
# nornir maps `docs-export = ["nornir-docgen/export"]`.
export = [
"dep:nornir-docbook",
"dep:typst",
"dep:typst-as-lib",
"dep:typst-svg",
"dep:pulldown-cmark",
"dep:chrono",
"dep:image",
"dep:quote",
"dep:proc-macro2",
]
[dependencies]
anyhow = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "1"
walkdir = "2"
syn = { version = "2", features = ["full", "extra-traits", "visit"] }
# Pages staging builds the orphan `pages` commit in-process (blobs + flat tree +
# ref update) — same pure-gix, no-subprocess ethos as nornir-git.
# gix 0.87, NOT 0.86 (forced 2026-08-27 by the registry, not by taste): every
# published version of `bisync` (0.1.0 … 0.3.1, all seven) is YANKED on
# crates.io, and `gix 0.86.0` requires `gix-protocol ^0.64.0`, whose only
# resolvable members (0.64.0, 0.65.0 — and ^0.64 cannot reach 0.65 anyway)
# require `bisync ^0.3.0`. Measured with a virgin CARGO_HOME against the real
# index:
#
# error: failed to select a version for the requirement `bisync = "^0.3.0"`
# version 0.3.0 is yanked
# version 0.3.1 is yanked
# required by package `gix-protocol v0.64.0`
# ... which satisfies dependency `gix = "^0.86"` of package `nornir-docgen v0.1.1`
#
# So `nornir-docgen 0.1.1` — the version that carries the `nornir:claim:` gate —
# could be DOWNLOADED but never RESOLVED by a registry consumer. `gix 0.87.1`
# requires `gix-protocol ^0.65.1`, the first release with no `bisync` edge at
# all, and there is no `gix 0.86.x` patch to escape into (0.86.0 is the whole
# line). The `tree-editor` FEATURE is gone in 0.87 — the tree editor
# (`.upsert`, used by `pages`) is unconditional there — so the feature list
# empties rather than shrinking.
gix = { version = "0.90" }
# Bench envelope types (BenchRun/BenchResult, direction/unit helpers, the ONE
# `_st` single-thread variant helper set — shared with nornir's core-saturation
# release gate, so it MUST stay in nornir-bench and is only consumed here).
nornir-bench = { version = "0.2.0", path = "../nornir-bench" }
nornir-cli-outcome = { version = "0.1.4", path = "../nornir-cli-outcome" }
# The CoverageRow POD the `coverage` section renders (pre-fetched by the caller;
# the Iceberg reader stays in nornir).
nornir-coverage = { version = "0.1.5", path = "../nornir-coverage" }
# Pure-gix local-config read/write (hooks) + HEAD sha/dirty (ai_context) +
# the gitio test helpers (pages).
# FLOOR 0.1.8, not 0.1.7 (2026-08-27): the same yanked-`bisync` break. Published
# `nornir-git` 0.1.5, 0.1.6 and 0.1.7 all require `gix ^0.86`, so all three are
# unresolvable from the index; 0.1.8 is the first on `gix ^0.87`. `^0.1.7` would
# resolve to 0.1.8 for a fresh build and to a DEAD 0.1.7 for anyone with a
# lockfile pinning it or building with minimal versions — the understated-floor
# trap, so the floor is the version that actually resolves.
nornir-git = { version = "0.2.0", path = "../nornir-git" }
# DWARF/callgraph/depgraph extraction + static SVG layers. Lives in the nornir
# repo's workspace.
#
# REGISTRY, NOT PATH (fixed 2026-08-25) — the third instance of the PATH IDENTITY
# failure already documented on `nornir-guard` in `nornir-release-core/Cargo.toml`
# and on `skidbladnir` in `nornir-build-thing/Cargo.toml`. `path =
# "../../../nornir/crates/nornir-introspect"` resolves relative to THIS manifest,
# so it always reaches the MAIN nornir checkout. nornir's own workspace lists
# `crates/nornir-introspect` as a member, so building nornir from a git WORKTREE
# shows cargo one package at two paths and it refuses to resolve at all:
#
# error: package collision in the lockfile: packages nornir-introspect v0.1.3
# (~/git/nornir/crates/nornir-introspect) and nornir-introspect
# v0.1.3 (<worktree>/crates/nornir-introspect) are different, but only one can
# be written to lockfile unambiguously
#
# Measured 2026-08-25 in TWO independent worktrees (`wt-models-qwen3` and the
# unrelated `wt-recur-nornir`): ONE line made nornir un-buildable from EVERY
# worktree, which is a standing tax on a fleet that works in worktrees by design.
#
# Note this is NOT the edda-back-edge shape `no_path_dep_on_our_consumer.rs`
# guards: that guard asks "does the target manifest name a package THIS workspace
# also defines?", and edda defines no `nornir-introspect`, so the edge is invisible
# to it. The invariant broken here is nornir's, not edda's, and nornir has no
# mirror-image guard — hence this line survived while two siblings were fixed.
#
# 0.1.3 is the floor because that is the version docgen was building against; its
# published source is byte-identical to the checkout it replaces (diffed 2026-08-25).
nornir-introspect = "0.1.3"
# ── `export` feature only ────────────────────────────────────────────────────
# The pure render engine (markdown → typst → PDF/HTML/SVG) — `crate::export`
# re-exports it so `book` keeps its `super::export` seam.
nornir-docbook = { version = "0.1.0", path = "../nornir-docbook", optional = true }
typst = { version = "0.15", optional = true }
typst-as-lib = { version = "0.16", features = ["typst-kit-fonts", "typst-kit-embed-fonts"], optional = true }
typst-svg = { version = "0.15", optional = true }
pulldown-cmark = { version = "0.13", default-features = false, features = ["html"], optional = true }
chrono = { version = "0.4", optional = true }
# `site`: measure the left-half picture's right edge for the page ground. Same
# version + features nornir-docbook already builds, so no new compile.
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp"], optional = true }
# apidoc pretty-prints extracted signatures via `quote::ToTokens` and reads
# item line numbers from spans (`span-locations` — same as nornir declared).
quote = { version = "1", optional = true }
proc-macro2 = { version = "1", features = ["span-locations"], optional = true }
[dev-dependencies]
tempfile = "3"
# book.rs's routing test builds a real `Nornir` config.
nornir-config = { version = "0.2.0", path = "../nornir-config" }