# Bengal 0.4.2

URL: /bengal/releases/0.4.2/
Section: releases
Description: Honest internals — failures now surface as build warnings instead of vanishing, file writes are crash-safe, the rebuild-reason vocabulary and parser contract are unified, config_path is honored, and a documentation sweep makes the reference match the code.

---

> For a complete page index, fetch /bengal/llms.txt.

**Key theme:** Honest internals. 0.4.2 is the second patch off 0.4.0 — thirteen backward-compatible fixes from the same repo-wide audit, plus the audit's own expansion pass. Where 0.4.1 stopped shipping *wrong* output, 0.4.2 stops *hiding* what goes wrong: failures that used to vanish at debug level now surface as build warnings, file writes can no longer be truncated by a crash, internal contracts and the documentation finally agree with the code, and the release tooling itself got fixed. No new features, no breaking changes.

## What's fixed

### Failures surface instead of vanishing

- **Search-index/JSON drops are now visible.** When per-page data accumulation failed, the page was silently dropped from the search index and JSON outputs at debug level. It now surfaces as a build warning (recorded in the build summary) with the full, untruncated error and a suggestion — the page is still skipped gracefully and the build still completes (`#449`).
- **No more silent `except` swallows.** Restoring cached parsed links that fails now logs `cached_links_restore_failed` (full error, `reason="fallback_to_empty"`) before falling back; and the dev server's build-trigger logs `template_cache_load_failed` instead of silently nulling the cache, so you can tell why an incremental rebuild stopped tracking template changes (`#472`).

### Your files can't be corrupted mid-write

- **Crash-safe writes.** Three remaining direct writes — the external-reference index cache, the performance `latest.json` snapshot, and the `bengal.yaml` rewrite on `bengal version` registration — now go through the atomic write-temp-then-rename helpers, so a SIGINT/crash/OOM mid-write can no longer leave a truncated file (`#471`).

### Internals that match their contracts

- **One rebuild-reason vocabulary.** The two diverged `RebuildReasonCode`/`RebuildReason` definitions are unified into a single canonical source; both import paths now resolve to the same objects, and `full_rebuild(reason=...)` records its reason instead of discarding it (`#445`).
- **`parse_with_toc` tells the truth.** The base contract now documents and returns the 4-tuple `(html, toc, excerpt, meta_description)` the parsers actually produce; `PythonMarkdownParser` returns four values too, so custom parsers and callers unpack uniformly (`#443`).
- **`Site.from_config(config_path=...)` is honored.** The documented `config_path` argument was silently dropped — Bengal always auto-discovered `bengal.toml`. The explicit path now loads, falling back to auto-discovery only when omitted (`#444`).
- **Dead and test-only code out of production.** Removed the never-called, divergent `phase_cache_save` finalization helper (`#451`) and a production `from unittest.mock import Mock` import in the index generator, replaced by a real serializability check (`#450`).

### Documentation matches the code

- **No fabricated CLI.** The invented `bengal project ...` command group is gone from the docs (skeletons apply during `bengal new site`; profiles via `bengal build --profile`), `bengal init` references corrected to `bengal config init`, and a new doc-lint fails CI if any `bengal <subcommand>` in the docs doesn't resolve to a registered command (`#435`).
- **Real content-types API.** The reference now shows `from bengal.content_types import register_strategy` with an instance (not the nonexistent `ContentTypeRegistry` class), and the "Available types" list matches the actual registry keys (`#436`).
- **Reality sweep.** Config docs use the real `baseurl` key (not `base_url`), stale `0.1.10` version examples updated, error-code docs name Kida (the default engine) instead of Jinja2, and the architecture reference lists the real `bengal/core/page` modules; the CLI-contract doc-lint allowlist was burned down (`bengal validate`→`check`, `collections`/`sources`→`content …`) (`#474`, `#470`).

### Tooling

- **Correct release titles.** `make gh-release` / `poe gh-release` built the GitHub release title from a greedy `grep '^name ='` that captured the towncrier category names — producing titles like `bengal Added Changed Deprecated Removed Fixed Security 0.4.1`. A new `scripts/publish_github_release.py` reads `[project].version` via `tomllib` and renders `vX.Y.Z — <theme>` (`#480`).

### Test integrity

- De-vacuumed a `hasattr(site.data, "tracks")` assertion (always true on a `DotDict`) into a real value check, and added `AssetURLValidator`/`PerformanceValidator` to the health-validator contract suite they were wrongly excluded from (`#473`).

## Changes at a glance

**Removed**

- Dead `phase_cache_save` build-finalization helper (`#451`).

**Fixed**

- Visible build warnings for JSON/search-index accumulation failures (`#449`) and previously silent `except` swallows (`#472`).
- Crash-safe atomic writes for the ref-cache, `latest.json`, and `bengal.yaml` (`#471`).
- Unified `RebuildReason`/`RebuildReasonCode` vocabulary (`#445`); 4-tuple `parse_with_toc` contract (`#443`); honored `Site.from_config(config_path=...)` (`#444`); removed a production `unittest.mock` import (`#450`).
- Documentation-vs-reality fixes: content-types API (`#436`), fabricated `bengal project` group (`#435`), `baseurl`/version/engine/module drift + CLI-lint allowlist (`#474`, `#470`).
- Correct GitHub release titles via a rewritten `gh-release` mechanism (`#480`).
- Test-integrity: de-vacuumed assertion + restored validator contract coverage (`#473`).

## Upgrading

```bash
pip install --upgrade bengal-ssg
```

0.4.2 is a drop-in patch: no breaking changes and no config migration. Everything here makes already-documented behavior actually work. If you parse build output, note that page-accumulation failures now appear as build warnings rather than being silently omitted; if you implement a custom parser, `parse_with_toc` now returns a 4-tuple `(html, toc, excerpt, meta_description)`.
