Audit hardening (S1+S2): numerics, newton_core refactor, solver diagnostics, validation #39

Merged
user2595 merged 16 commits from fix/b1-v3-c1-quick-wins into main 2026-05-31 22:16:38 +00:00
10 changed files with 254 additions and 16 deletions
Showing only changes of commit 74d41e99a8 - Show all commits

View File

@@ -37,6 +37,21 @@ a concrete fix, and acceptance criteria.
> submission. Referenced from three audits: `cgal-submission-readiness` (G0/G1),
> `dependency-license` (D1/D2 — incl. the unlicensed test meshes), and indirectly
> `math-derivation-citation` (provenance of the Sechelmann-2016 primary source).
>
> **🔄 Status 2026-05-31:** the original authors (Sechelmann / Springborn,
> TU Berlin / Varylab) have been **contacted by email** to clarify
> porting/relicensing rights. **Awaiting reply.** Until a written answer exists,
> all G0-gated work (G1G12, D1/D2, A4/A5, public release, CGAL submission)
> stays paused.
## 🗺️ Working plan across sessions & models
See [`finding-orchestration.md`](finding-orchestration.md) — the meta-plan that
maps every finding to a session, an implementing model (Haiku/Sonnet/Opus), and
an Opus review gate. **Session 1 (2026-05-31) is complete:** 26 findings
resolved across Sonnet/Haiku/Opus, 298/298 CGAL tests green (branch
`fix/b1-v3-c1-quick-wins`). The orchestration doc lists the remaining sessions
S2S6.
## Correctness & port fidelity
@@ -45,33 +60,44 @@ a concrete fix, and acceptance criteria.
| [`external-audit-2026-05-30.md`](external-audit-2026-05-30.md) | Correctness bugs | 3 findings (face_energy, GaussBonnet, cotangent doc) — **all resolved** |
| [`java-port-audit.md`](java-port-audit.md) | Java→C++ math fidelity | line-by-line port anomalies — mostly resolved |
| [`java-ignore-crossvalidation.md`](java-ignore-crossvalidation.md) | `@Ignore` cross-validation | which Java tests were intentionally skipped and why |
| [`numerical-stability-audit-2026-05-31.md`](numerical-stability-audit-2026-05-31.md) | FP robustness | N1: FD-Hessian step (`1e-5`) fights Newton tol (`1e-8`); clamps, cancellation, magic constants |
| [`math-derivation-citation-audit-2026-05-31.md`](math-derivation-citation-audit-2026-05-31.md) | Derivations & citations | M1 KolpakovMednykh missing from refs; M2 BPS cited "2010" vs "2015" |
| [`numerical-stability-audit-2026-05-31.md`](numerical-stability-audit-2026-05-31.md) | FP robustness | **N3/N4/N5/N6 ✅** (clamp mode, named constants, Kahan area); **N1 ✅\*** subsumed by B1 block-FD; **N2/N7 open** |
| [`math-derivation-citation-audit-2026-05-31.md`](math-derivation-citation-audit-2026-05-31.md) | Derivations & citations | **M1/M2/M4 ✅** (refs + year + citation format); **M3 open**, **M5 needs expert** |
## API, performance & packaging
| Document | Focus | Headline |
|---|---|---|
| [`api-performance-audit-2026-05-31.md`](api-performance-audit-2026-05-31.md) | API naming + Newton perf | A1A5 naming (**decided**, see below); B1: block-FD Hessian exists but solver uses slow full-FD |
| [`cgal-submission-readiness-audit-2026-05-31.md`](cgal-submission-readiness-audit-2026-05-31.md) | CGAL submission | G0 (rights), G1 (MIT≠GPL), layout, concept, user manual, test harness |
| [`dependency-license-audit-2026-05-31.md`](dependency-license-audit-2026-05-31.md) | Deps + data licensing | D1 matrix predicated on the G0-undermined MIT premise; D2 test meshes have no provenance |
| [`api-performance-audit-2026-05-31.md`](api-performance-audit-2026-05-31.md) | API naming + Newton perf | **A1A3 ✅**, **B1 ✅** (both halves), **B2B5 ✅** (newton_core refactor); **A4/A5 deferred** (G0/G1) |
| [`cgal-submission-readiness-audit-2026-05-31.md`](cgal-submission-readiness-audit-2026-05-31.md) | CGAL submission | **⛔ blocked by G0** (author contacted); G1 (MIT≠GPL), layout, concept, manual, test harness all pending |
| [`dependency-license-audit-2026-05-31.md`](dependency-license-audit-2026-05-31.md) | Deps + data licensing | **⛔ blocked by G0**; D1 matrix predicated on the G0-undermined MIT premise; D2 test meshes have no provenance |
| [`usability-audit-2026-05-31.md`](usability-audit-2026-05-31.md) | Examples & docs | U1U11 — **all resolved** (PR #34) |
## Robustness & operational
| Document | Focus | Headline |
|---|---|---|
| [`test-coverage-error-handling-audit-2026-05-31.md`](test-coverage-error-handling-audit-2026-05-31.md) | Coverage + error handling | C1 solver `ok` flag dropped; coverage gate **decided: 80 % line / 70 % branch / 90 % func** |
| [`input-validation-audit-2026-05-31.md`](input-validation-audit-2026-05-31.md) | Malformed input | V3: NaN/Inf vertex coords flow silently into the solver |
| [`thread-safety-audit-2026-05-31.md`](thread-safety-audit-2026-05-31.md) | Concurrency | no mutable static state (good); same-mesh concurrency contract undocumented |
| [`test-coverage-error-handling-audit-2026-05-31.md`](test-coverage-error-handling-audit-2026-05-31.md) | Coverage + error handling | **C1/C2/C3 ✅**, **I2/I3/I4 ✅**, **H2 ✅** (newton_core); **I1/I5/H1/H3/H4/H5 open**; gate in ramp-up until I5 |
| [`input-validation-audit-2026-05-31.md`](input-validation-audit-2026-05-31.md) | Malformed input | **V1/V2/V3/V4 ✅** (NaN/Inf guard + JSON/XML error handling); **V5/V6 open** (polish) |
| [`thread-safety-audit-2026-05-31.md`](thread-safety-audit-2026-05-31.md) | Concurrency | no mutable static state (good); same-mesh concurrency contract **still undocumented (open, S4)** |
## Decisions taken (2026-05-31)
## Session 1 — implemented (2026-05-31)
- **Coverage gate:** 80 % line / 70 % branch / 90 % function (after fixing the
coverage script so it measures honestly — `test-coverage` C2/I5).
Branch `fix/b1-v3-c1-quick-wins`, 9 commits, **298/298 CGAL tests green**.
See [`finding-orchestration.md`](finding-orchestration.md) for the full plan.
- **Sonnet:** B1(HyperIdeal)/V3/C1; C2/C3 (coverage gate, ramp-up); V1/V2/V4; I2/I3/I4.
- **Haiku:** N4/N6 (named constants); M1/M2/M4 (citations); A1A3 (naming + `[[deprecated]]` aliases).
- **Opus:** B1 inversive-distance block-FD port; N5 (Kahan area); N3 (selectable
clamp mode `HardJava`|`SmoothBarrier`); H2 + B2/B3/B4/B5 (newton_core refactor).
- **🔍 Opus review:** all Sonnet/Haiku commits validated (value-identity, error
handling, coverage-gate logic, alias forwarding) — all correct.
### Decisions standing
- **Coverage gate:** 80 % line / 70 % branch / 90 % function. Script now measures
honestly (C2 ✅); gate is in **ramp-up** (`SKIP_COVERAGE_GATE=1`) until **I5**
wires it to the full CGAL suite.
- **API naming A1A3 (internal):** standardized on `<verb>_<geom>_<rest>` with
`[[deprecated]]` aliases — **implemented**, open as **PR #36**
(`refactor/api-naming-a1-a3`), 277/277 tests green.
`[[deprecated]]` aliases — **implemented**.
- **API naming A4A5 (public CGAL surface):** decided (`discrete_inversive_distance_euclidean`;
both packers return `Circle_packing_result`) but **deferred** until the G0/G1
outcome — no point stabilizing a public API on a package whose release status is open.
@@ -79,9 +105,10 @@ a concrete fix, and acceptance criteria.
## How to act on a finding in a new session
Open the relevant audit, pick a finding, follow its "Fix" + "Acceptance criteria".
Build/verify with the commands in that document's header. Suggested first batch
(low-risk, high-value quick wins): **B1** (HyperIdeal block-FD one-liner), **B3**,
**C1**, **C2**, **V3**.
Build/verify with the commands in that document's header. **The Session-1 quick-win
batch (B1, B3, C1, C2, V3, …) is done** — for what to pick up next, follow the
session plan in [`finding-orchestration.md`](finding-orchestration.md): the next
pending batch is **S2** (I1 + H1 + N7, Sonnet→Opus review).
## When to send to the reviewer

View File

@@ -14,6 +14,12 @@ act on it without prior context. Each finding includes:
Status legend: 🔴 Critical · 🟡 Important · 🔵 Hint / nice-to-have
> **✅ Resolution status (2026-05-31, Session 1):** **A1A3 ✅** (renamed with
> `[[deprecated]]` aliases), **B1 ✅** both halves (HyperIdeal + Inversive-Distance
> block-FD), **B2/B3/B4/B5 ✅** (folded into the `newton_core` refactor — see
> test-coverage **H2**). **A4/A5 deferred** until the G0/G1 licensing outcome
> (public CGAL surface). 298/298 tests green. Plan: [`finding-orchestration.md`](finding-orchestration.md).
> **Companion documents (no overlap in findings):**
> - `external-audit-2026-05-30.md` — port-faithfulness bugs
> - `test-coverage-error-handling-audit-2026-05-31.md` — test gaps + error handling

View File

@@ -12,6 +12,13 @@ act on it without prior context.
Status legend: ⛔ Blocker (submission rejected on sight) · 🔴 Critical · 🟡 Important · 🔵 Polish
> **🔄 Status (2026-05-31):** the whole package — **G0** (porting rights), and
> everything it gates (**G1G12**) — is **blocked pending the original authors'
> reply**. The authors have been **contacted by email** asking to clarify
> porting/relicensing rights. No layout/license/header work proceeds until a
> written answer exists. Scheduled as **S6** (Opus, after G0/G1) in
> [`finding-orchestration.md`](finding-orchestration.md).
> **Companion documents:**
> - `external-audit-2026-05-30.md` — port-faithfulness bugs
> - `test-coverage-error-handling-audit-2026-05-31.md` — test gaps + error handling

View File

@@ -9,6 +9,12 @@ unresolved provenance/license blocker (CGAL audit **G0/G1**)?
Status legend: 🔴 Critical · 🟡 Important · 🔵 Polish
> **🔄 Status (2026-05-31):** **D1/D2 blocked by G0** — the dependency/licensing
> matrix rests on the MIT premise that G0 (porting rights) undermines. The
> original authors have been **contacted by email**; no licensing action until
> their reply. Scheduled **S6** (after G0/G1) in
> [`finding-orchestration.md`](finding-orchestration.md).
> **Good news up front:** a `code/deps/THIRD-PARTY-LICENSES.md` already exists and is
> a genuinely good per-dependency compatibility matrix (CGAL, Eigen, libigl, GLFW,
> nlohmann/json). This audit does **not** redo that work. It flags two things that

View File

@@ -0,0 +1,159 @@
# Finding Orchestration — Sessions × Models × Review Gates
**Updated:** 2026-05-31
**Purpose:** a structured plan to work through every audit finding across
**multiple short sessions**, each run by the **model best suited** to the work,
with an **external Opus reviewer session after every implementation session**.
This lets you pick up any "Session" below cold, hand it to the named model, and
know exactly what it covers and how its output gets validated.
> Companion: the per-finding detail lives in the 11 audit documents in this
> folder (see [`README.md`](README.md)). This file is the *meta-plan* — the
> order, the model assignment, and the review cadence.
---
## How to use this
1. Pick the next **⬜ pending** session from the [sequence](#session-sequence).
2. Start a session with the **assigned model**, point it at the listed findings
in their audit docs (each finding has `file:line`, fix, acceptance criteria).
3. When it finishes, start the paired **🔍 Opus review session** — it validates
the diff (correctness, value-identity, tests, no parity regression) and only
then is the batch "done".
4. Mark the session ✅ here and move on.
Rationale for the cadence: implementation models (Sonnet/Haiku) are cheaper and
fast for well-specified work; Opus is reserved for (a) findings that need real
numerical/architectural judgement and (b) the **review gate**, because an
independent high-capability pass catches subtle parity/numerics regressions that
a self-review from the implementing model tends to miss.
---
## Model-assignment heuristic
| Model | Take findings that are… | Examples |
|---|---|---|
| **Haiku** | mechanical, local, low-risk; docs, citations, renames, named-constant extraction | M-series, N4/N6, A1A3, doc files |
| **Sonnet** | normal coding with a clear acceptance criterion; tests, error-handling, CI, small API changes | V-series, C-series, I-series, H-series |
| **Opus** | numerics, math correctness, architecture, public-API/irreversible decisions — **and every review gate** | B1-port, N1/N3/N5/N7, H2 refactor, A4/A5, all 🔍 reviews |
> Decision rule isn't "how severe" but "how much must be *understood* to get it
> right." A 🔴 one-line switch can be Sonnet; a 🟡 numerical reformulation is Opus.
---
## Master finding table
Status: ✅ done · ⬜ open (actionable) · ⏸ deferred (intentional) · ⛔ blocked (G0) · 👤 needs human expert
| ID | Audit | Sev | Status | Model | Session |
|----|-------|-----|--------|-------|---------|
| B1 (HyperIdeal) | api-perf | 🔴 | ✅ | Sonnet | S1 |
| B1 (Inv-Dist port) | api-perf | 🔴 | ✅ | Opus | S1 |
| V3 | input-val | 🟡 | ✅ | Sonnet | S1 |
| C1 | test-cov | 🔴 | ✅ | Sonnet | S1 |
| N4, N6 | numerics | 🟡/🔵 | ✅ | Haiku | S1 |
| M1, M2, M4 | math-cite | 🟡/🔵 | ✅ | Haiku | S1 |
| C2, C3 | test-cov | 🔴 | ✅ | Sonnet | S1 |
| V1, V2, V4 | input-val | 🟡 | ✅ | Sonnet | S1 |
| I2, I3, I4 | test-cov | 🟡 | ✅ | Sonnet | S1 |
| A1, A2, A3 | api-perf | 🔴/🟡 | ✅ | Haiku | S1 |
| N5 | numerics | 🟡 | ✅ | Opus | S1 |
| N3 | numerics | 🟡 | ✅ | Opus | S1 |
| H2, B2, B3, B4, B5 | test-cov/api-perf | 🔵/🟡 | ✅ | Opus | S1 |
| **I1** | test-cov | 🟡 | ⬜ | Sonnet→🔍Opus | **S2** |
| **H1** | test-cov | 🔵 | ⬜ | Sonnet→🔍Opus | **S2** |
| **N7** | numerics | 🔵 | ⬜ | Opus | **S2** |
| **H3** | test-cov | 🔵 | ⬜ | Sonnet→🔍Opus | **S3** |
| **H4** | test-cov | 🔵 | ⬜ | Sonnet→🔍Opus | **S3** |
| **H5** | test-cov | 🔵 | ⬜ | Sonnet→🔍Opus | **S3** |
| **V5, V6** | input-val | 🔵 | ⬜ | Sonnet→🔍Opus | **S3** |
| **N2** | numerics | 🟡 | ⬜ | Haiku→🔍Opus | **S4** |
| **thread-safety doc** | thread-safety | 🟡 | ⬜ | Haiku→🔍Opus | **S4** |
| **M3** | math-cite | 🟡 | ⬜ | Haiku→🔍Opus | **S4** |
| **I5** | test-cov | 🟡 | ⬜ | Sonnet→🔍Opus | **S5** |
| A4, A5 | api-perf | 🟡 | ⏸ | Opus | S6 (after G0/G1) |
| N1 | numerics | 🔴 | ✅* | — | (subsumed by B1; doc note folds into N2/S4) |
| G1G12 | cgal | ⛔/🔴 | ⛔ | Opus | (after G0) |
| D1, D2 | dep-license | 🔴 | ⛔ | Opus | (after G0) |
| M5 | math-cite | 🔵 | 👤 | — | human domain expert |
| G0 | cgal | ⛔ | 🔄 in progress | owner | **author contacted by email — awaiting reply** |
\* N1 (FD-step vs Newton tol) is largely subsumed by the B1 block-FD work; the
remaining piece is the tolerance-coupling note, folded into N2 (Session S4).
---
## Session sequence
### ✅ S1 — Audit quick-wins + numerics + refactor (DONE, 2026-05-31)
Branch `fix/b1-v3-c1-quick-wins`, 9 commits, 298/298 CGAL tests green.
- **Sonnet:** B1(HyperIdeal)/V3/C1; C2/C3, V1/V2/V4, I2/I3/I4.
- **Haiku:** N4/N6; M1/M2/M4; A1A3.
- **Opus:** B1 inv-dist port; N5; N3; H2+B2/B3/B4/B5.
- **🔍 Opus review:** validated all Sonnet/Haiku commits (value-identity, error
handling, coverage gate, alias forwarding) — all correct.
### ⬜ S2 — Solver result diagnostics (Sonnet → 🔍 Opus)
- **I1** — give `NewtonResult` a status enum
(`Converged`/`MaxIterations`/`LinearSolverFailed`/`LineSearchStalled`). The
`newton_core` refactor centralised every exit point, so this lands in one
place now. Update the 5 wrappers + any callers reading `converged`.
- **H1** — `res.iterations` is stale when the solver breaks on `!ok`/stall;
set it correctly at each `break` in `newton_core`.
- **N7** *(Opus within this session)* — optional conditioning diagnostic
(smallest LDLT pivot / cheap cond estimate) surfaced in the result; pairs
naturally with the I1 status enum (near-singular ⇒ a distinct status/flag).
- **🔍 Opus review:** confirm the enum doesn't break the CGAL-layer result
mapping and that `converged` semantics are unchanged for existing callers.
### ⬜ S3 — Robustness & test-gap closure (Sonnet → 🔍 Opus)
- **H3** — `enforce_gauss_bonnet` reports the magnitude of the applied correction.
- **H4** — test `reduce_to_fundamental_domain` boundary `Im(τ)==0.0`.
- **H5** — integration test for the degenerate-triangle path in the Newton solve.
- **V5** — make the hand-rolled XML reader *reject* reformatted-but-valid XML
instead of silently mis-reading it into zeros.
- **V6** — DOF-vector-vs-mesh size check on load.
- **🔍 Opus review:** verify the new rejections don't break valid round-trips.
### ⬜ S4 — Documentation & citations (Haiku → 🔍 Opus)
- **N2** — `doc/math/tolerances.md`: every numerical threshold, its role, and
the constraints between them (incl. the N1 FD-step ≪ Newton-tol coupling).
- **thread-safety** — document the same-mesh concurrency contract (the code has
no mutable static state; this is purely the written contract).
- **M3** — re-verify the future-dated / arXiv-only citations against their
published versions; update `references.md`.
- **🔍 Opus review:** sanity-check the tolerance relationships are stated correctly.
### ⬜ S5 — Coverage truthfulness (Sonnet → 🔍 Opus)
- **I5** — wire `coverage.sh` to the full CGAL suite (not just the 26 fast
tests, ~9.6%), then remove `SKIP_COVERAGE_GATE=1` so the agreed
80%/70%/90% gate goes live (C3 ramp-up → enforced).
- **🔍 Opus review:** confirm the measured numbers are honest and the gate fails
correctly when coverage drops.
### ⏸ S6 — Public CGAL surface + packaging (Opus, **only after G0/G1**)
Gated by the author's reply on porting/relicensing rights.
- **A4, A5** — public entry-point renames + unified `Circle_packing_result`.
- **G1G12, D1, D2** — license headers, CGAL package layout, concept doc, user
manual, CGAL test harness, dependency/data provenance.
- **G0 status:** *author contacted by email (2026-05-31), awaiting reply.* No
public release / submission / relicensing until a written answer exists.
### 👤 Out of model scope
- **M5** — the large hand-derivations need a domain-expert prose review
(the numerical results are already validated; the prose is not).
---
## Review-gate checklist (what every 🔍 Opus session checks)
- [ ] Builds clean; full CGAL suite green (no count regression).
- [ ] No Java golden-vector / parity test perturbed (HardJava defaults intact).
- [ ] Numeric changes are value-identical where claimed, or justified + tested.
- [ ] New public surface (result types, enums, API) is intentional and documented.
- [ ] Commit message attributes the implementing model
(`Co-Authored-By: Claude <Model> <noreply@anthropic.com>`).
- [ ] Finding marked ✅ in the master table above with the commit ref.

View File

@@ -10,6 +10,12 @@ which covered *internal* error propagation and the deliberate `throw` paths.
Status legend: 🔴 Critical · 🟡 Important · 🔵 Polish
> **✅ Resolution status (2026-05-31, Session 1):** **V1/V2/V4 ✅** (JSON parse +
> field checks, XML stoi/stod guarded — all surface as `std::runtime_error` with
> path), **V3 ✅** (NaN/Inf vertex-coordinate guard in `load_mesh`). **V5** (XML
> strict-reject) and **V6** (DOF-size check) **open** — scheduled S3 in
> [`finding-orchestration.md`](finding-orchestration.md). 298/298 tests green.
> **Threat model:** this is a scientific library, not a network service, so the bar
> is "fail cleanly and diagnosably", not "resist attackers". But meshes and result
> files routinely come from other tools, other people, and older versions of this

View File

@@ -10,6 +10,13 @@ checks code/docs vs. *the published mathematics*.
Status legend: 🔴 Critical · 🟡 Important · 🔵 Polish
> **✅ Resolution status (2026-05-31, Session 1):** **M1 ✅** (KolpakovMednykh +
> Meyerhoff/Ushijima added to `references.md`), **M2 ✅** (BPS year unified to the
> published 2015), **M4 ✅** (citation format normalized). **M3** (re-verify
> future-dated/arXiv citations) **open** → S4; **M5** (prose derivation review)
> needs a **domain expert** (out of model scope). See
> [`finding-orchestration.md`](finding-orchestration.md).
> **Good news up front:** `doc/math/references.md` is unusually scholarly and already
> contains several *self-corrections* (the Glickenstein "eq. (4.6)" numbering fix →
> §5.2 parametrization; the Schläfli boundary-term re-attribution to RivinSchlenker

View File

@@ -10,6 +10,13 @@ port-faithfulness (`java-port-audit.md`) and from internal error propagation
Status legend: 🔴 Critical · 🟡 Important · 🔵 Polish
> **✅ Resolution status (2026-05-31, Session 1):** **N3 ✅** (selectable clamp mode
> `HardJava`|`SmoothBarrier`), **N4/N6 ✅** (named `constexpr` constants), **N5 ✅**
> (Kahan stable triangle area). **N1 ✅\*** largely subsumed by the B1 block-FD
> Hessian; the remaining FD-step/tol coupling note folds into **N2**. **N2** (tolerances.md)
> and **N7** (conditioning diagnostic) **open** — scheduled S2/S4 in
> [`finding-orchestration.md`](finding-orchestration.md). 298/298 tests green.
> **Relationship to existing audits:** `java-port-audit.md` already verified several
> per-formula numerical edge cases (degenerate triangles #1, overflow centring #10).
> This audit takes the *cross-cutting* view those miss: how the tolerances relate to

View File

@@ -14,6 +14,14 @@ act on it without prior context. Each finding includes:
Status legend: 🔴 Critical · 🟡 Important · 🔵 Hint / nice-to-have
> **✅ Resolution status (2026-05-31, Session 1):** **C1 ✅** (solver `ok` flag
> exposed), **C2/C3 ✅** (coverage script honest + CI gate in ramp-up),
> **I2/I3/I4 ✅** (serialization / mesh / spherical-hessian negative tests),
> **H2 ✅** (five Newton loops unified into `newton_core`, folding in B2/B3/B4/B5).
> **Open:** **I1** (status enum) + **H1** (stale iterations) → S2; **H3/H4/H5** → S3;
> **I5** (coverage on full suite, un-gates the 80/70/90 threshold) → S5.
> See [`finding-orchestration.md`](finding-orchestration.md). 298/298 tests green.
> **Note:** This audit is complementary to `external-audit-2026-05-30.md` (which
> covers port-faithfulness bugs). There is no overlap in findings — this one is
> strictly about *test coverage* and *error-handling robustness*.

View File

@@ -9,6 +9,11 @@ documented? Currently undocumented everywhere.
Status legend: 🔴 Critical · 🟡 Important · 🔵 Polish
> **Status (2026-05-31):** no code change needed (no mutable static state — good).
> The one open item is **documenting the same-mesh concurrency contract** —
> scheduled **S4** (Haiku→Opus review) in
> [`finding-orchestration.md`](finding-orchestration.md).
> **Good news up front:** a scan for mutable function-local `static` variables (the
> classic hidden-shared-state hazard) found **none**. All `static` in the headers are
> stateless factory/accessor functions (`MobiusMap::identity`,