Solution strategy

Five decisions shape everything else about zdocs. Each is stated here as the approach taken; the reasoning lives in the linked ADRs.

Consumed as a Zephyr module, by name

The engine is reached through Zephyr’s module system — ZEPHYR_ZDOCS_MODULE_DIR on the module path, then include(zdocs) — so no consumer writes a path into the engine and the same engine serves any number of projects. Everything the engine needs from the project arrives through the ZDOCS_* variables. See 0001. zdocs is a Zephyr module, included by name.

One registry, derived everywhere

documents.yaml describes the set once: every document’s kind, group, cross-reference prefix, source folder, builders and feature opt-ins. Build targets, intersphinx mappings, Doxygen tag file lists, navigation entries, needs imports and group aggregates are all derived from it. A consumer declares a document in exactly one place. See 0004. The registry is the single source of truth for document declarations.

Everything is built twice

Cross-references between documents are circular by nature: A cannot resolve against B’s index until B has produced one, and vice versa. Rather than ordering documents — which cannot work for a cycle — the engine builds the whole set in two stages. Stage one produces every document’s index; stage two rebuilds everything against the complete set. See Cross-cutting concepts.

Two toolchains, one presentation

Sphinx and Doxygen documents are peers. They share a navigation sidebar computed from the same registry, the same version-resolution rules and the same branding hooks, and they cross-reference each other in both directions — doxylink from Sphinx into Doxygen symbols, tag files and a navigation widget from Doxygen back into the prose.

The engine takes ownership of the small number of tool settings this requires, overriding a consumer’s own values for those keys, and leaves every other setting to the consumer.

The test suite is a consumer

The engine’s primary tests are a separate project that consumes zdocs the way a real one does, builds real documentation, and asserts on rendered output. The engine additionally carries a fast unit suite for its pure-Python parts. See 0002. Acceptance tests live in a consumer repository, over one cumulative fixture and 0003. The engine carries its own unit suite, in the engine repository.