# API surface history One file per released `v3.*` tag: `.json`, e.g. `v3.12.0.json`. Each is the output of `extract_api.py --surface-output` at that tag (see `tools/api_checker/README.md` for the schema), with additional immutable provenance in `meta`: `ref`, `commit` (the tag's resolved commit sha), `generated_at`, and `generator`. ## Conventions - **Immutable once committed.** Files here are never hand-edited or silently regenerated. `snapshot_release.py` refuses to overwrite an existing file unless `--force` is passed, and that should only happen for a deliberate, reviewed fix — the resulting diff should be inspected before committing, same as any other source change. - **Generated by `tools/api_checker/snapshot_release.py`**, run manually as part of cutting a release (see `tools/api_checker/README.md`'s "Workflow: Release Checklist"). Not CI-automated. - **`diff_api.py` uses these automatically.** `--old`/`--new` check here first (matching the ref string to `.json`) before falling back to live `git archive` extraction — see `tools/api_checker/README.md`'s `diff_api.py` section. ## Coverage Backfilled: every `v3.*` tag from `v3.1.0` through the latest release at backfill time (`v3.12.0`), covering the full public API history of the `include/nlohmann/` header layout. ## Format history - **`format_version: 2`** (current). Fixed `extract_api.py`'s `ABI_TAG_PATTERN` to also strip the pre-rename ABI inline-namespace form used by `v3.11.0`/`v3.11.1`: `json_v3_11_0` (no `_abi` segment), renamed to today's `json_abi_v3_11_2`-style tag starting with `v3.11.2`. Under `format_version: 1`, that older form wasn't recognized, so every `scope`/`signature` at `v3.11.0`/`v3.11.1` retained the raw un-stripped namespace segment, making `diff_api.py` report essentially the entire API (~330 of ~332 entries) as removed-and-readded across `v3.10.5`->`v3.11.0`->`v3.11.1`->`v3.11.2` — a bug of the same shape as the earlier USR-arity issue documented in `extract_api.py`'s `identity_key()` docstring, caught the same way: by actually diffing real consecutive release pairs instead of trusting the extractor in isolation. All 27 files were regenerated under `format_version: 2`; only `v3.11.0.json` and `v3.11.1.json` actually changed content (every other tag's scopes never contained the old-style tag). - **`format_version: 3`**. Found via a CI run on a different machine than the one that generated `format_version: 2`, which is exactly the failure class this versioning exists to catch: 1. `JSON_HAS_RANGES` auto-detects via the standard library's `__cpp_lib_ranges` feature-test macro. That macro isn't reliably gated to C++20 mode by every stdlib -- undefined under `-std=c++17` with macOS's libc++, but defined under the identical flag with the Ubuntu stdlib used in CI -- so `parse()`/`accept()`/`from_bjdata()`/etc. extracted a different signature (with or without a `SentinelType` parameter) purely depending on which machine ran the extraction. Now pinned to `-DJSON_HAS_RANGES=0` in `extract_api.py`'s parse arguments: the deterministic choice, since forcing `1` was tried first and found to actually fail to parse on a stdlib without full `` support even when the macro claims otherwise. 2. `get_identity_name()` used `cursor.spelling` verbatim for `CONVERSION_FUNCTION` cursors, which libclang renders as its own internally-canonicalized form of the return type rather than what's literally written. Confirmed for `json_pointer::operator string_t()`: spelled `"operator nlohmann::json_pointer::string_t_helper::type"` on one machine and `"operator typename string_t_helper::type"` on another, with both machines pinned to the identical `libclang==18.1.1` wheel and the JSON_HAS_RANGES fix above ruled out as the cause. Now derived from the cursor's own raw source text instead (regex over `get_signature_text()`'s output), immune to libclang's dependent-type resolution differences and incidentally more readable (`"operator string_t"`/`"operator ValueType"` instead of the libclang-internal forms). All 27 files were regenerated under `format_version: 3`. ## Known gaps - **`v3.0.0`, `v3.0.1`**: not backfilled. These predate the `include/nlohmann/` directory structure entirely — headers lived under `src/` at that point (a single `src/json.hpp`). Since `extract_api.py` hardcodes `include/nlohmann/json.hpp` as the entry point, `snapshot_release.py --all-tags` fails cleanly on these two refs (`git archive ... -- include` finds nothing) rather than silently producing a wrong/empty result. Not pursued: two tags, immediately superseded by `v3.1.0`, and supporting the pre-restructuring layout would need a separate header/include-path convention with no other benefit. If full pre-3.1 coverage is ever wanted, `extract_api.py` would need a `src/json.hpp`-aware mode first. - Pre-`v3.0.0` tags (`v1.x`, `v2.x`, `v3.0.0-rc*`, etc.) were never attempted — out of scope for this backfill; see `tools/api_checker/POLICY.md` for the stated boundary.