# `DocShell.Build`
[🔗](https://github.com/futhr/doc_shell/blob/v2.0.0/lib/doc_shell/build.ex#L1)

Runs every extractor and writes the complete artifact tree to disk.

This is the top of the pipeline. `run/1` resolves configuration, extracts
modules, guides, notebooks, changelog entries, and the OpenAPI document,
projects the result into navigation and search indexes, and writes the lot as
versioned JSON. `mix doc_shell.build` is a thin wrapper around it.

    {:ok, result} =
      DocShell.Build.run(
        modules: [MyApp.Accounts, MyApp.Billing],
        guide_bases: ["guides", "handbook"]
      )

Options are merged over host configuration and package defaults, in that
order — see `DocShell.Config`. Passing nothing is valid and produces an empty
but well-formed tree, which keeps a first integration from being a
configuration exercise.

## Failing loudly

Extraction stops at the first error and returns `{:error, reason}`, with the
offending module or file path in the reason. A guide with broken frontmatter
is not skipped and a module whose docs fail to parse does not quietly vanish
from the navigation. Documentation that silently loses a page is worse than
documentation that fails to build, because nobody notices the former until a
reader does.

## The return value

`run/1` returns the complete extraction, keyed by `:modules`, `:guides`,
`:livebooks`, `:changelog`, `:openapi`, and `:presentation`. Hosts that ingest
documentation into a database or knowledge graph should use this rather than
reading the JSON back off disk — it is richer than what gets written, since
entries here keep their parsed `ast` and nothing is filtered out. Pass
`write: false` to skip the files entirely.

When `:collection` is configured, the return value also includes the
normalized collection descriptor and source provenance used for
`collection.json`.

## Choosing a presentation producer

`:changelog_source` selects the module that loads release notes. The default
source reads `CHANGELOG.md`, but graph-backed hosts can point it at their own
adapter and pass source-specific `:changelog_options`.

`:presentation_source` selects the module that builds navigation, search, and
content, defaulting to `DocShell.Presentation.StaticGenerator`. A
graph-backed host points it at their own
`DocShell.Presentation.GraphProjector` and the pipeline validates whatever
comes back. `:path_builder`, `:skip_empty`, `:search_tokens`, and `:search_members` are passed
through to the producer.

## Written files

Artifacts land in the configured `:public_dir` and `:private_dir`, each with
a `manifest.json` describing the directory it sits in.

What is written is leaner than what is returned. The per-source artifacts —
`modules.json` and friends — carry an entry's identity and metadata but not
its parsed body, which lives once in `content.json` under the same id.
Writing the AST in both places doubled the tree for no reader. Likewise,
module member documentation is written once, in `modules.json`; navigation
carries only member references.

Every artifact is encoded once, as compact canonical JSON. When a collection
is configured, its artifact digests are computed from those same bytes
rather than by encoding each payload again. Set `pretty: true` to indent the
files for debugging.

See the [artifact contract notebook](artifact-contract.html) for the full tree.

# `run`

```elixir
@spec run(keyword()) :: {:ok, map()} | {:error, term()}
```

Extracts, projects, and writes every configured documentation artifact.

`overrides` takes precedence over host configuration; see `DocShell.Config`
for the recognised keys. Returns the extracted data on success and the first
error encountered otherwise.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
