diff --git a/CMakeLists.txt b/CMakeLists.txt
index c51fa3f..cc25109 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -100,100 +100,22 @@ file(WRITE ${CMAKE_BINARY_DIR}/DartConfiguration.tcl
add_subdirectory(tests)
# ---------------------------------------------------------------------
-# Code reference (Doxygen) — optional 'docs' target
+# Documentation
+#
+# The Doxygen configuration and the 'docs' target are defined in
+# docs/CMakeLists.txt. The paths it builds into are named here, above
+# that directory, so each is written once and the clean rule below
+# refers to the same values.
+#
+# The subdirectory is given its own binary directory so that CMake's
+# scaffolding stays out of the documentation output.
# ---------------------------------------------------------------------
-option(DPM_DOCS_HTML "Generate the code reference in HTML" ON)
-option(DPM_DOCS_PDF "Generate the code reference as PDF (requires LaTeX)" ON)
+set(DPM_DOCS_DIR ${CMAKE_BINARY_DIR}/docs)
+set(DPM_DOCS_TMP_DIR ${DPM_DOCS_DIR}/tmp)
+set(DPM_DOCS_PDF_DIR ${DPM_DOCS_DIR}/pdf)
+set(DPM_DOCS_HTML_DIR ${DPM_DOCS_DIR}/html)
-find_package(Doxygen)
-if(DOXYGEN_FOUND)
- # Doxygen and LaTeX both work in scratch space under docs/tmp. The
- # finished document is copied up into docs/, so the directory a user
- # opens holds documentation and nothing else.
- set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs/tmp)
- set(DOXYGEN_EXTRACT_ALL YES)
- set(DOXYGEN_EXTRACT_STATIC YES)
- set(DOXYGEN_QUIET YES)
- set(DOXYGEN_WARN_IF_UNDOCUMENTED NO)
- set(DOXYGEN_JAVADOC_AUTOBRIEF YES)
-
- if(DPM_DOCS_HTML)
- set(DOXYGEN_GENERATE_HTML YES)
- else()
- set(DOXYGEN_GENERATE_HTML NO)
- endif()
-
- if(DPM_DOCS_PDF)
- set(DOXYGEN_GENERATE_LATEX YES)
- set(DOXYGEN_USE_PDFLATEX YES)
- set(DOXYGEN_PDF_HYPERLINKS YES)
- else()
- set(DOXYGEN_GENERATE_LATEX NO)
- endif()
-
- # The prose documents, each rendered as its own page in the generated
- # reference. Named one per line so the set of documents reaching the
- # output is stated here instead of implied by a directory.
- set(DPM_DOC_PAGES
- ${CMAKE_CURRENT_SOURCE_DIR}/docs/OVERVIEW.md
- ${CMAKE_CURRENT_SOURCE_DIR}/docs/DESIGN.md
- ${CMAKE_CURRENT_SOURCE_DIR}/docs/CONSUMERS.md
- ${CMAKE_CURRENT_SOURCE_DIR}/docs/MODULES.md
- ${CMAKE_CURRENT_SOURCE_DIR}/docs/BUILD.md
- ${CMAKE_CURRENT_SOURCE_DIR}/docs/DOCUMENTATION.md
- )
-
- # Two kinds of input: include/ and src/ supply the API and internals
- # reference extracted from the source, DPM_DOC_PAGES supplies the
- # prose pages.
- #
- # One target, so `cmake --build
--target docs` is the whole
- # procedure and the IDE lists a single entry for it. The commands
- # below run after Doxygen and place the finished documents.
- doxygen_add_docs(docs
- ${CMAKE_CURRENT_SOURCE_DIR}/include
- ${CMAKE_CURRENT_SOURCE_DIR}/src
- ${DPM_DOC_PAGES}
- COMMENT "Generating the reference with Doxygen"
- )
-
- if(DPM_DOCS_PDF)
- find_program(PDFLATEX_EXECUTABLE pdflatex)
- if(PDFLATEX_EXECUTABLE)
- add_custom_command(TARGET docs POST_BUILD
- COMMAND make -C ${CMAKE_BINARY_DIR}/docs/tmp/latex
- COMMAND ${CMAKE_COMMAND} -E rm -rf
- ${CMAKE_BINARY_DIR}/docs/pdf
- COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_BINARY_DIR}/docs/tmp/latex/refman.pdf
- ${CMAKE_BINARY_DIR}/docs/pdf/dpm-core-${PROJECT_VERSION}.pdf
- COMMENT "Compiling the PDF reference"
- )
- else()
- message(WARNING "DPM_DOCS_PDF is ON but pdflatex was not found; no PDF will be produced")
- endif()
- endif()
-
- if(DPM_DOCS_HTML)
- add_custom_command(TARGET docs POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E rm -rf
- ${CMAKE_BINARY_DIR}/docs/html
- COMMAND ${CMAKE_COMMAND} -E copy_directory
- ${CMAKE_BINARY_DIR}/docs/tmp/html
- ${CMAKE_BINARY_DIR}/docs/html
- COMMENT "Placing the HTML reference"
- )
- endif()
-
- # Doxygen keeps output files it judges unchanged, so a run over a
- # populated scratch directory can carry pages forward from an earlier
- # one. Removing it here means the next run regenerates everything,
- # and leaves docs/ holding the finished documents alone.
- add_custom_command(TARGET docs POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMAKE_BINARY_DIR}/docs/tmp
- COMMENT "Clearing the generators' scratch directory"
- )
-endif()
+add_subdirectory(docs ${CMAKE_BINARY_DIR}/docs-build)
# ---------------------------------------------------------------------
# Clean
@@ -214,7 +136,7 @@ set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES
${CMAKE_BINARY_DIR}/lib
${CMAKE_BINARY_DIR}/modules
${DPM_TEST_DIR}
- ${CMAKE_BINARY_DIR}/docs
+ ${DPM_DOCS_DIR}
)
# ---------------------------------------------------------------------
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644
index 0000000..122e9d3
--- /dev/null
+++ b/docs/CMakeLists.txt
@@ -0,0 +1,98 @@
+# ---------------------------------------------------------------------
+# Code reference (Doxygen) — optional 'docs' target
+#
+# The output paths come from the top-level file, which defines them
+# before adding this directory so that each is written in one place.
+# ---------------------------------------------------------------------
+option(DPM_DOCS_HTML "Generate the code reference in HTML" ON)
+option(DPM_DOCS_PDF "Generate the code reference as PDF (requires LaTeX)" ON)
+
+find_package(Doxygen)
+if(DOXYGEN_FOUND)
+ # Doxygen and LaTeX both work in scratch space under docs/tmp. The
+ # finished document is copied up into docs/, so the directory a user
+ # opens holds documentation and nothing else.
+ set(DOXYGEN_OUTPUT_DIRECTORY ${DPM_DOCS_TMP_DIR})
+ set(DOXYGEN_EXTRACT_ALL YES)
+ set(DOXYGEN_EXTRACT_STATIC YES)
+ set(DOXYGEN_QUIET YES)
+ set(DOXYGEN_WARN_IF_UNDOCUMENTED NO)
+ set(DOXYGEN_JAVADOC_AUTOBRIEF YES)
+
+ if(DPM_DOCS_HTML)
+ set(DOXYGEN_GENERATE_HTML YES)
+ else()
+ set(DOXYGEN_GENERATE_HTML NO)
+ endif()
+
+ if(DPM_DOCS_PDF)
+ set(DOXYGEN_GENERATE_LATEX YES)
+ set(DOXYGEN_USE_PDFLATEX YES)
+ set(DOXYGEN_PDF_HYPERLINKS YES)
+ else()
+ set(DOXYGEN_GENERATE_LATEX NO)
+ endif()
+
+ # The prose documents, each rendered as its own page in the generated
+ # reference. Named one per line so the set of documents reaching the
+ # output is stated here instead of implied by a directory.
+ set(DPM_DOC_PAGES
+ ${CMAKE_CURRENT_SOURCE_DIR}/PROSE/OVERVIEW.md
+ ${CMAKE_CURRENT_SOURCE_DIR}/PROSE/DESIGN.md
+ ${CMAKE_CURRENT_SOURCE_DIR}/PROSE/CONSUMERS.md
+ ${CMAKE_CURRENT_SOURCE_DIR}/PROSE/MODULES.md
+ ${CMAKE_CURRENT_SOURCE_DIR}/PROSE/BUILD.md
+ ${CMAKE_CURRENT_SOURCE_DIR}/PROSE/DOCUMENTATION.md
+ )
+
+ # Two kinds of input: include/ and src/ supply the API and internals
+ # reference extracted from the source, DPM_DOC_PAGES supplies the
+ # prose pages.
+ #
+ # One target, so `cmake --build --target docs` is the whole
+ # procedure and the IDE lists a single entry for it. The commands
+ # below run after Doxygen and place the finished documents.
+ doxygen_add_docs(docs
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/src
+ ${DPM_DOC_PAGES}
+ COMMENT "Generating the reference with Doxygen"
+ )
+
+ if(DPM_DOCS_PDF)
+ find_program(PDFLATEX_EXECUTABLE pdflatex)
+ if(PDFLATEX_EXECUTABLE)
+ add_custom_command(TARGET docs POST_BUILD
+ COMMAND make -C ${DPM_DOCS_TMP_DIR}/latex
+ COMMAND ${CMAKE_COMMAND} -E rm -rf
+ ${DPM_DOCS_PDF_DIR}
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${DPM_DOCS_TMP_DIR}/latex/refman.pdf
+ ${DPM_DOCS_PDF_DIR}/dpm-core-${PROJECT_VERSION}.pdf
+ COMMENT "Compiling the PDF reference"
+ )
+ else()
+ message(WARNING "DPM_DOCS_PDF is ON but pdflatex was not found; no PDF will be produced")
+ endif()
+ endif()
+
+ if(DPM_DOCS_HTML)
+ add_custom_command(TARGET docs POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E rm -rf
+ ${DPM_DOCS_HTML_DIR}
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+ ${DPM_DOCS_TMP_DIR}/html
+ ${DPM_DOCS_HTML_DIR}
+ COMMENT "Placing the HTML reference"
+ )
+ endif()
+
+ # Doxygen keeps output files it judges unchanged, so a run over a
+ # populated scratch directory can carry pages forward from an earlier
+ # one. Removing it here means the next run regenerates everything,
+ # and leaves docs/ holding the finished documents alone.
+ add_custom_command(TARGET docs POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E rm -rf ${DPM_DOCS_TMP_DIR}
+ COMMENT "Clearing the generators' scratch directory"
+ )
+endif()
diff --git a/docs/BUILD.md b/docs/PROSE/BUILD.md
similarity index 100%
rename from docs/BUILD.md
rename to docs/PROSE/BUILD.md
diff --git a/docs/CONSUMERS.md b/docs/PROSE/CONSUMERS.md
similarity index 100%
rename from docs/CONSUMERS.md
rename to docs/PROSE/CONSUMERS.md
diff --git a/docs/DESIGN.md b/docs/PROSE/DESIGN.md
similarity index 99%
rename from docs/DESIGN.md
rename to docs/PROSE/DESIGN.md
index eea8501..2500e3c 100644
--- a/docs/DESIGN.md
+++ b/docs/PROSE/DESIGN.md
@@ -186,12 +186,13 @@ include/dpm/ public headers — installed to the system include path; the
dpm/ directory is the consumer namespace, so an installed
consumer writes #include
include/internal/ library-private headers — used only by src/, never installed
-src/ implementations of the library
+src/core/ implementations of the library
src/cli/ the dpm binary's entry point
src/bundled-modules/info/ the bundled info module
data/ files installed as-is (core.conf)
tests/ fixture modules, the API test binary, CLI tests
-docs/ project documentation
+docs/PROSE/ the project's written documentation
+docs/ the Doxygen configuration and the docs target
```
Every header lives under include/: include/dpm/ is the published API surface and defines what consumers see; include/internal/ is the implementation's own headers, invisible outside the repo because the install rule ships only include/dpm/.
diff --git a/docs/DOCUMENTATION.md b/docs/PROSE/DOCUMENTATION.md
similarity index 77%
rename from docs/DOCUMENTATION.md
rename to docs/PROSE/DOCUMENTATION.md
index 21406aa..3308b78 100644
--- a/docs/DOCUMENTATION.md
+++ b/docs/PROSE/DOCUMENTATION.md
@@ -1,8 +1,6 @@
# Generating the Code Reference {#documentation}
-When Doxygen is present, the build offers a `docs` target that generates the API and source reference from the documentation comments carried in the headers and sources. It opens on a front page declared in `include/dpm/core.h`, followed by the module contract, then the namespace, class, and file reference.
-
-The markdown documents in this directory are written and read on their own and stay out of the generated reference.
+When Doxygen is present, the build offers a `docs` target that generates the API and source reference from the documentation comments carried in the headers and sources, together with the documents in `docs/PROSE/` as its pages. `src/documentation.cpp` declares which documents those are and in what order; `docs/CMakeLists.txt` configures the generator and defines the target.
One command generates it:
diff --git a/docs/MODULES.md b/docs/PROSE/MODULES.md
similarity index 100%
rename from docs/MODULES.md
rename to docs/PROSE/MODULES.md
diff --git a/docs/OVERVIEW.md b/docs/PROSE/OVERVIEW.md
similarity index 100%
rename from docs/OVERVIEW.md
rename to docs/PROSE/OVERVIEW.md
diff --git a/docs/STYLE.md b/docs/PROSE/STYLE.md
similarity index 100%
rename from docs/STYLE.md
rename to docs/PROSE/STYLE.md
diff --git a/src/documentation.cpp b/src/documentation.cpp
index b912b59..02a78dc 100644
--- a/src/documentation.cpp
+++ b/src/documentation.cpp
@@ -9,7 +9,7 @@
*
* Each entry in the main page names a document by its page label,
* declared on the first heading of the corresponding markdown file in
- * docs/.
+ * docs/PROSE/.
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
* @author Chris Punches