---
title: Bengal 0.4.0
description: Documentation generation grows up — S-tier Python and OpenAPI autodoc, byte-reproducible parallel builds, a more reliable dev server, a stabilized default theme with versioned theme libraries, and an experimental opt-in render-at-scale backend.
date: 2026-06-12
draft: false
lang: en
tags: [release, changelog, autodoc, openapi, performance, theme, dev-server]
keywords: [release, 0.4.0, autodoc, openapi, reproducible-builds, render-isolation, theme-libraries, dev-server]
category: changelog
---

# Bengal 0.4.0

**Key theme:** Documentation generation grows up. Bengal 0.4.0 turns autodoc from "it renders" into a real reference experience — Python API docs cross-link symbols, group overloads, and show inheritance; REST/OpenAPI docs become a filterable, navigable explorer. Around that headline, builds are now **byte-reproducible** and meaningfully faster, the `bengal serve` dev loop is more reliable, the bundled theme is stabilized, and there's an early, **opt-in** backend for rendering large sites across separate process heaps.

---

## Highlights

### Python API docs that cross-link, group overloads, and show inheritance

Autodoc output is now genuinely navigable:

- **Symbol cross-linking.** Return types, parameter types, base classes, `See Also` targets, and bare `Name` code spans in docstrings now link to the documented symbol's page. A deterministic resolver maps symbols to page-aware URLs (modules to their page, classes/functions/methods to a stable `#card` anchor), so links never 404. Ambiguous simple names resolve to nothing rather than guessing — a wrong link is worse than no link. The previously-dropped `See Also` section now renders.
- **`@overload` grouping.** Multiple `typing.overload` stubs plus the implementation collapse into a *single* documented member that lists every signature variant, with an `overload` badge — instead of N+1 duplicate peers colliding on one anchor.
- **Inherited-member badges.** With `include_inherited` enabled, members synthesized from base classes now render an "inherited from X" attribution badge, visually distinct from native members.
- Members that don't get their own page (classes, functions, methods, CLI options) now get an on-page anchor `href` instead of a dangling page URL.

### REST / OpenAPI docs become a real explorer

- **Advanced schema rendering.** `oneOf` / `anyOf` / `allOf` render as labeled composition blocks; polymorphic schemas show their `discriminator` and mapping; validation constraints (`format`, `pattern`, numeric and length bounds, `uniqueItems`, …) render as chips; `nullable` / `readOnly` / `writeOnly` / `deprecated` render as badges; and self-referential schemas show a bounded "circular reference" indicator instead of an empty box or runaway recursion.
- **A page per endpoint, by default.** Every endpoint now gets its own three-column explorer page (the `consolidate` option defaults to `false`), so endpoint cards link to real pages instead of dead anchors. Set `consolidate: true` for the old reference-view.
- **Catalog navigation.** The API landing catalog and schema index gained client-side filtering, a scroll-spy left rail, and copy buttons on operation paths — one lazy-loaded vanilla-JS enhancement, **no npm**, fully degrading with JS disabled and honoring `prefers-reduced-motion`.

### Faster — and byte-reproducible — builds

- **Reproducible output.** Identical content now produces identical output across repeated builds *and across worker counts*. Related-posts tie-breaks, tag-listing order, and tag accent colors are all deterministic now, making parallel free-threaded output trustworthy for caching, CDNs, and version control.
- **Render hot path ~27% faster** single-threaded (section-path memoization eliminated ~265k `pathlib` calls on a 300-page docs build).
- **Related posts ~8× faster** on large sites (4,288 pages: ~24s → ~3s), with byte-identical output.
- **Asset-dependency extraction ~4× faster** via a single-pass scanner, and the post-render asset audit now parallelizes (autodoc builds ~1.7× overall).
- **Warm dev-loop saves**: the provenance dependency index now updates incrementally instead of re-reading every record — ~180× cut to that save phase on a 500-page blog.

### A more reliable `bengal serve`

The dev-server "theme drops to unstyled HTML" class of bugs is fixed:

- **Hidden-buffer asset 404s fixed.** When the active output buffer was the hidden `.bengal/staging` directory, Pounce's static handler 404'd *every* CSS/JS/font request while HTML kept loading — the classic fully-rendered-but-unstyled page. A live repro went from 53% of CSS requests 404ing to 0%.
- **`sendfile` EAGAIN crash fixed** (the `BlockingIOError: [Errno 35]` seen serving static assets on macOS + free-threaded 3.14t).
- **Asset-manifest buffer drift fixed**, plus a startup HTTP serve-ability smoke check that fails loudly if a known asset can't be fetched — catching serve-path regressions before the first request.

### One stable default theme + versioned theme libraries

- The bundled `default` theme is **stabilized**: dead/demo assets removed, experimental holographic-card CSS is now feature-detected/opt-in (no longer force-loaded), and pagination/TOC glow animations honor `prefers-reduced-motion`.
- The in-repo `chirpui` bridge theme is **removed** — component-library integration (e.g. Chirp UI) now ships as external theme packages through the same library-provider contract.
- **Theme-library version governance.** Theme libraries can declare a `contract_version` and a `requires` map of PEP 440 specifiers; Bengal enforces both at build start with a clear `BengalConfigError` (C003) and a fix command, instead of failing on the first rendered page. A `bengal[chirp]` extra pins compatible versions.

### Experimental: render at scale across process heaps (opt-in, off by default)

Bengal 0.4.0 lands the foundation for rendering large cold builds across **separate-heap worker processes**, to recover the free-threading render plateau. It is **off by default** behind `[build] render_isolation`, scoped to cold CLI/CI builds only (the dev server and incremental builds keep the in-process thread path), produces byte-identical output, and falls back to threads on any failure — so it can never break a build. It's not yet a guaranteed end-to-end win on every site, which is exactly why it stays opt-in. Tracking continues in #350.

---

## Added

- Python autodoc: symbol cross-linking, `@overload` grouping, and inherited-member badges (#327, #328, #329).
- REST/OpenAPI autodoc: advanced schema rendering and first-class catalog navigation (#285, #287).
- Experimental, opt-in isolated render backend for large cold builds (`render_isolation`, off by default) (#350).
- Theme-library version governance — `contract_version` + `requires` enforcement and a `bengal[chirp]` extra (#363).
- Dev-server startup serve-ability smoke check and a live `bengal serve` HTTP integration test (#398, #399).
- A theming guide ("What Bengal Provides vs What Your Theme Provides"), a REST autodoc visual-QA checklist, `.github/CODEOWNERS`, and a CI import-contract gate.

## Changed

- OpenAPI autodoc Phase 2: a page per endpoint by default, full consolidated tag-page references, cross-endpoint sidebar, and multi-tag cross-listing (#287 and follow-ups).
- Builds are faster: related-posts (~8×), render hot path (~27%), asset-extraction (~4×), parallel asset audit (~1.7× on autodoc), and incremental provenance save (~180×).
- The bundled `default` theme is stabilized — dead assets removed, holo CSS feature-detected, reduced-motion respected.
- Theme-library provider resolution is cached per `(site_root, theme_chain)`, shared across all build paths and shard workers.
- The experimental shard-render byte-parity tests now run as a nightly signal instead of gating every PR (#376); the functional shard tests stay merge gates.
- Standardized CLI render-format vocabulary so `json` is the consistent machine-readable token and `--help` shows one format option, not two.
- Reduced internal coupling to the legacy `Page` class (retired public `bengal.Page` re-exports; deleted the legacy mutable `Page` module).

## Fixed

- **Builds are byte-reproducible** across repeated builds and worker counts (#reproducible-builds).
- Dev server: hidden-buffer asset 404s (unstyled-page bug), `sendfile` EAGAIN crashes, and asset-manifest buffer drift (#392, #400, #314, #315).
- Warm/incremental builds now keep `rss.xml`, the Lunr `search-index.json`, and the asset manifest correct (no more shrinking-to-one-entry or stale feeds).
- The internal taxonomy snapshot is no longer always-empty, restoring the lock-free tag-page fast path and correct per-language tag pages under i18n.
- Cleared the dogfood-site health blockers (broken nav links, missing icons, unregistered `important` admonition); health rose 60% → 80% (#288).
- Snapshot parse cache now keys on parser version + config hash; rendered-output cache resolves template deps against the site root.
- Faster CLI help — `--version` and single-command runs skip full parser construction.
- Numerous docs-accuracy corrections (parser availability, custom-directives API, benchmark figures) with import/contract guards.

## Removed

- The experimental in-repo `chirpui` bridge theme — Bengal now bundles a single stable `default` theme (#remove-chirpui-bridge).
- Dead REST/OpenAPI layout CSS (~1450 lines) superseded by the current explorer shell, plus stale skipped tests and dead packages/metadata.

---

## Upgrading

```bash
uv pip install --upgrade bengal
# or
pip install --upgrade bengal
```

Most changes are additive or internal. A few things to know:

- **OpenAPI endpoint pages** now generate one page per endpoint by default. If you relied on the consolidated reference view, set `consolidate: true` in your OpenAPI autodoc config.
- **The `chirpui` bridge theme was removed.** If you used it, switch to an external theme package built on the theme-library provider contract.
- **Theme-library authors:** declare `contract_version` and `requires` in your `get_library_contract()` so version skew fails fast with a clear message at build start.
- **The isolated render backend is off by default.** It's experimental; opt in with `[build] render_isolation` only for large cold CLI/CI builds, and expect a thread fallback on any failure.
