libxml-rs 0.1.0-alpha.18

Native-Rust forensic reimplementation of libxml2+libxslt with C ABI drop-in replacement. Cross-version oracle matrix (libxml2 2.7.8-2.15.3, libxslt 1.1.26-1.1.45) with semantic epochs correlated to upstream commits; full xmllint/xmlcatalog/xsltproc CLIs; differential-court-verified C API closure (xmlXPath*, xmlTextReader*, xmlTextWriter*, catalogs, serialization, data globals, chvalid, encoding, the full libxslt surface) — parity ledger at 0 missing for both libxml2 and libxslt. 1135 tests passing.
Documentation
# libxml-rs

**Phase 11: Historical matrix — semantic epochs for libxml2/libxslt behavior.**

Custodial native-Rust reimplementation of the **libxml2 + libxslt** ecosystem:
a forensic reconstruction of observable behavior, implemented in native Rust,
with C ABI compatibility for drop-in replacement.

This is **not** an XML crate. This is **not** an XSLT crate. This is **not** a wrapper.
This is a custodial forensic archive and native-Rust reimplementation of
the complete observable behavior of libxml2 and libxslt across their
historical lifetimes.

---

## Current Status: Phase 11 — Historical matrix (§85)

Phase 11 delivers the cross-version archaeology of **§41 (historical oracle matrix), §42
(version fingerprints), §51 (multi-version triangulation)**. Per §85:

> *Deliverable: The project can explain how current behavior came to exist.*

A configurable oracle matrix (`oracle/historical/`) builds 12 historical libxml2
releases (2.7.8 → 2.15.0) plus 5 libxslt releases (1.1.26 → 1.1.42) from the archaeology
git clones and runs a 28-case behavioral corpus against every oracle and the system
2.15.3/1.1.45 binaries, capturing byte-exact stdout/stderr/exit and per-version sha256
fingerprints:

- **`oracle/historical/build.sh`** — era-tolerant oracle builder (era tag spellings, autotools modernizations, `--without-threads` for modern glibc)
- **`oracle/historical/run_matrix.sh`** — matrix runner + per-case epoch grouping; `results/matrix.json` holds version→case→sha256
- **`atlas/SEMANTIC_EPOCHS.md`** — the epoch map: 10 behavioral epochs (E-001…E-008 + exit-code epochs) correlated with the exact upstream commits/NEWS that created them
- **`courts/suites/historical/HIST-EPOCH-*.json`** — §43 casefiles; **`courts/receipts/historical-matrix-2026-08-29.json`** — §44 receipt

Headline findings (all correlated to upstream commits in the atlas):

- `xmllint --xpath` node-set output gained newline separators in **2.9.10** (commit `da35eeae`, an upstream-documented breaking change)
- The 2.9.10 parser regression (`EndTag: '</' not found`) was fixed in **2.9.11** (`de5b624f`); the second parse-error diagnostic was dropped entirely in **2.12**
- A **2.13.0** cluster: parser/validation exit codes reworked (1→4, 4→3), entity-in-attribute errors reported twice, entity-decl debug nodes became `TEXT compact` (`8d04f0ee`)
- **2.15.0**: `--html` dumps became single-line (newline writes removed from `HTMLtree.c`); `--valid` with no DTD returns exit 0
- libxslt core transform output is a **stable epoch** — byte-identical from 1.1.26 (2009) to 1.1.45

### Phase 10 (historical)

Phase 10 delivered the **`xmllint`** and **`xmlcatalog`** command-line tools
(§36):

Both tools are faithful native-Rust ports of the upstream programs, verified
byte-for-byte against the system libxml2 2.15.3 binaries:

- **`xmllint` CLI**`--debug`, `--copy`, `--format`, `--valid`/`--postvalid`, `--dtdvalid`, `--xpath`/`--xpath0`, `--xinclude`, `--html`/`--xmlout`, `--noent`, `--no-compact`, `--encode`, `--recover`, `--dropdtd`, `--pedantic`, `--noout`, `--quiet`, `--nonet`, `--huge`, … with upstream exit codes (0/1/3/4) and the upstream `file:line: parser error : MSG` + source-line/caret diagnostics
- **`xmlcatalog` CLI**`--create`, `--add`, `--del`, `--resolve` (system/public/URI), `--noout`, `--shell` (interactive `resolve`/`system`/`public`/`add`/`del`/`dump`/`help`/`exit`), SGML→XML catalog conversion, upstream exit codes
- **Compact text nodes** — the parser reproduces libxml2's `XML_PARSE_COMPACT` inline text storage (≤15 bytes in the node struct), so `--debug` dumps show `TEXT compact` exactly like the oracle; merged/entity-interrupted text is correctly non-compact
- **DTD validation diagnostics**`--valid` output (messages, caret placement, exit codes) matches the oracle for both no-DTD and declaration errors
- **Entity expansion**`--noent` re-parses declared-entity content through the input stack (nested references and markup entities included), matching upstream trees
- **HTML serialization** — meta-charset insertion, upstream formatting rules (p/pre/param never formatted, single-child and inline elements inline), HTML document headers
- **1110 passing tests**: `cargo test --lib` — 0 failures
- **Differential oracle parity**: a 44-case CLI suite (`target/difftest_summary.sh`) is **byte-identical** to the system tools (stdout + stderr + exit codes), plus a 30+ case edge corpus (entities, DTDs, HTML, compact/no-compact, debug dumps, XPath)

### Phase 9 (historical)

Phase 9 delivered the **complete EXSLT module set** (§35) and the
**`xsltproc` command-line tool** (§36):

All seven EXSLT modules are implemented natively and registered through the
process-wide EXSLT registry (`exsltRegisterAll`, mirroring upstream):

- **`exsl:` Common**`exsl:node-set` (with real result-tree-fragment support), `exsl:object-type`
- **`math:`**`math:max`, `math:min`, `math:sin`, `math:cos`, `math:tan`, `math:constant`, `math:power`, `math:sqrt`, `math:log`, `math:random`, …
- **`set:`**`set:difference`, `set:intersection`, `set:distinct`, `set:has-same-node`, `set:leading`, `set:trailing`
- **`str:`**`str:tokenize`, `str:padding`, `str:concat`, `str:split`, `str:replace`, `str:align`, `str:encode-uri`, `str:decode-uri`
- **`dyn:`**`dyn:evaluate`, `dyn:element`, `dyn:attribute`, `dyn:map`, `dyn:call`, …
- **`date:`**`date:date-time`, `date:date`, `date:time`, `date:year`, `date:month-in-year`, `date:day-in-month`, `date:day-of-week-in-month`, `date:format-date`, `date:add`, `date:difference`, `date:seconds`, `date:day-name`, …
- **`func:`**`func:function`, `func:result` (extension-function declarations)
- **`exsltRegisterAll` C ABI export** — mirrors upstream; `xsltproc` calls it at startup
- **`xsltproc` CLI** — full option surface (`--param`, `--stringparam`, `--output`, `--noout`, `--html`, `--encoding`, `--xinclude`, `--profile`, `--maxdepth`, `--maxvars`, `--nonet`, `--nowrite`, …) with upstream exit codes (1–11)
- **RTF support** — variables with inline content become context-owned result tree fragments; `exsl:node-set($var)/path` navigation works
- **1110 passing tests**: `cargo test --lib` — 0 failures
- **Differential oracle parity**: a 12-case `xsltproc` corpus (basic transform, `count()`/AVTs, `exsl:node-set`/`math:`/`set:`/`str:`, predicates, attribute string-values, `xsl:if`/`xsl:when`, numbering, descending `xsl:sort`, `key()`, `call-template` with params, `method="html"`) is **byte-identical** to the system libxslt 1.1.45 `xsltproc` (stdout + exit codes)

### Underlying subsystem fixes landed during Phase 9

| Fix | Surface | Detail |
|-----|---------|--------|
| XPath core functions in XSLT | xslt | The transform context now registers the XPath 1.0 core function library — before, every XPath function call (`count()`, `substring()`, …) failed as unknown |
| AVT evaluation | xslt | `{expr}` attribute value templates evaluated in literal attributes and `xsl:element`/`xsl:attribute`/`xsl:processing-instruction` names (XSLT 1.0 §7.6.2) |
| RTF variable ownership | xslt | Inline variable content is deep-copied into a context-owned RVT (freed exactly once at context teardown) — fixes a double-free and enables `exsl:node-set` |
| Node string-value | tree | `node_get_content` concatenates all descendant text (XPath 1.0 string-value), not just direct text children |
| Caller parameter format | xslt | `xsltApplyStylesheet` params parsed as upstream `(name, value)` pairs with `{uri}name` namespace form |
| `date:` no-arg default | exslt | `date:date()`/`date:time()` and the component functions default to the current date-time (EXSLT spec) |

### Phase 8 (historical)

Phase 8 implemented the complete native-Rust **XSLT 1.0 engine**: stylesheet
compilation, template matching, pattern compilation, variable/parameter binding,
keys, sorting, numbering, imports/includes, extensions, security, serialization,
and the transform runtime (§31–§34). The XSLT engine operates exclusively on
the Rust libxml implementation — no upstream libxml2/libxslt is loaded, linked,
or shelled out to (§31):

```text
Rust CLI → Rust libxslt compatibility layer → Rust XSLT engine
        → Rust XPath implementation → Rust libxml tree/parser/serializer
```

Subsystem fixes landed during Phase 8:

| Fix | Surface | Detail |
|-----|---------|--------|
| SAX2 namespace resolution | parser | `startElementNs` receives split prefix/localname + resolved URIs; element and attribute namespaces now attached by the tree builder |
| Absolute path evaluation | XPath | `/root/item` evaluates from the document node, not the root element (XPath 1.0 `/` semantics) |
| XPath C ABI helpers | ABI | `xmlXPathObjectCopy`, `xmlXPathCastToString`, `xmlXPathCastStringToNumber`, `xmlXPathCmpNodes`, `xmlXPathNodeSetCreate` |
| Node content getter | tree | `node_get_content` (upstream `xmlNodeGetContent` semantics) |
| Template-content ownership | xslt | Template content belongs to the stylesheet document; `xsltFreeTemplate` no longer double-frees it |
| `node()`/`text()` patterns | xslt | Bare node-test calls translate to steps with correct §5.5 priorities |
| `match="/"` semantics | xslt | Matches only the document node, not the root element |

All six Phase 8 residuals are documented in [`atlas/RESIDUAL_LEDGER.md`](atlas/RESIDUAL_LEDGER.md) (R-000101–R-000106).

### Build

```sh
cargo build                          # Build library + CLI binaries
cargo build --lib                    # Build only the library
cargo test --lib                     # Run library tests (1110 passing)

# Test C consumer compilation against our headers:
gcc -I include courts/suites/sanity/ABI-STRUCT-NODE-0001-abicheck.c -o /tmp/abicheck
clang -I include courts/suites/sanity/ABI-ENUM-0001-enumcheck.c -o /tmp/enumcheck

# Build and run oracle container:
docker build -f docker/Dockerfile.oracle -t libxml-rs/oracle:2.12.0 docker/
```

### Published artifacts

- crates.io: [`libxml-rs`]https://crates.io/crates/libxml-rs `0.1.0-alpha.15`
- GitHub: <https://github.com/infinityabundance/libxml-rs>

### Oracle verification

The oracle Docker container builds libxml2 2.12.0 and libxslt 1.1.39 from source.
The candidate (`libxml-rs`) does not link against system libxml2/libxslt — verified
by the oracle contamination court.

---

## Test coverage by subsystem

| Subsystem | Tests | Subsystem | Tests | Subsystem | Tests |
|-----------|------:|-----------|------:|-----------|------:|
| XPath 1.0 | 128 | URI | 69 | Encoding | 65 |
| XML Schema (XSD) | 62 | DTD validation | 56 | RELAX NG | 56 |
| XML parser + SAX | 50 | Regex | 44 | I/O | 44 |
| Schematron | 40 | DTD | 35 | XML Reader | 35 |
| Entities | 31 | HTML | 31 | C14N | 29 |
| Tree/ownership | 28 | XInclude | 23 | XML Writer | 20 |
| Automata | 16 | Catalog | 16 | XPointer | 15 |
| Debug | 11 | Hash | 9 | List | 8 |
| Dictionary | 7 | Globals | 7 | String | 6 |
| Errors | 5 | Memory | 2 | Threads | 2 |
| ABI allocator | 8 | | | | |
| XSLT patterns | 46 | XSLT transform | 26 | XSLT security | 7 |
| XSLT numbering | 5 | XSLT stylesheet | 4 | XSLT variables/params | 9 |
| XSLT compiler | 3 | XSLT sorting | 3 | XSLT misc (keys/space/serial/ns/imports/ext) | 13 |
| EXSLT dates | 9 | EXSLT strings | 6 | EXSLT math | 5 |
| EXSLT common | 4 | EXSLT sets | 4 | EXSLT dynamic | 2 |
| EXSLT functions | 1 | EXSLT registry | 3 | XSLT params | 5 |
| **Total (1110 passing, 1 ignored)** | | | | | |

---

## Project Structure

```
libxml-rs/
├── Cargo.toml              # Single Cargo package (no workspace)
├── src/
│   ├── lib.rs              # Library entry point
│   ├── abi/                # C ABI compatibility layer (§4, §14)
│   ├── xml/                # libxml2 implementation (§1, §3, §31)
│   ├── xslt/               # libxslt implementation (§31–§34)
│   ├── exslt/              # EXSLT modules (§35)
│   ├── compatibility/      # Historical profiles, quirks, platform (§68, §69)
│   └── bin/                # CLI tools: xmllint, xmlcatalog, xsltproc (§36)
├── include/                # Compatible C headers (§15)
├── atlas/                  # Forensic archive (§7–§12)
│   ├── releases/           # Release manifests per version
│   ├── api/                # Public API inventories
│   ├── abi/                # ABI snapshots
│   ├── symbols/            # Symbol table comparisons
│   ├── config/             # Build configuration profiles
│   ├── standards/          # Standards mapping
│   ├── HISTORY.md          # Complete release history
│   ├── LORE.md             # Undocumented behavior archive
│   ├── QUIRKS.md           # Confirmed compatibility quirks
│   ├── PARITY_MATRIX.md    # Current parity status
│   ├── RESIDUAL_LEDGER.md  # Unexplained differences
│   └── SECURITY_HISTORY.md # Vulnerability custody
├── oracle/                 # Reproducible upstream build environment (§39)
├── courts/                 # Differential testing framework (§40–§50)
│   ├── schema.json         # Casefile schema
│   ├── suites/             # Court case suites
│   ├── receipts/           # Execution receipts
│   └── tools/              # Court runner
├── tools/                  # Archaeology and analysis tooling
│   ├── archaeology/        # manifest.py, apiatlas.py, symbols.py, delta.py, profileconfig.py
│   └── courts/             # Court runner
├── docker/                 # Reproducible Docker oracle images
├── docs/                   # Technical documentation
└── archaeology/            # Upstream git clones (immutable, offline)
```

---

## Build

```sh
cargo build              # Build the library and CLI binaries
cargo build --lib        # Build only the library
cargo test --lib         # Run library tests (1110 passing)
cargo build --release    # Optimized build (LTO, panic=abort)
```

The crate builds as `cdylib` + `staticlib` + `rlib`; the build script also
emits `libxml-2.0.pc`/`libxslt.pc` pkg-config files, `xml2-config`/`xslt-config`
scripts, and SONAME symlinks into the target directory.

---

## License

Licensed under either of:
- MIT license ([LICENSE-MIT]LICENSE-MIT)
- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE)

at your option.

---

## Project Status

| Dimension | Status |
|-----------|--------|
| API completeness | 🟢 libxml2 surfaces complete; libxslt ABI (33 symbols) exported |
| ABI compatibility | 🟢 ABI courts passing (struct, symbol, enum) |
| Tree/ownership | 🟢 28 tree tests passing |
| XML parser + SAX | 🟢 50 parser tests; SAX2 namespace resolution |
| XPath 1.0 | 🟢 128 tests, absolute paths, node sets, conversions |
| Validation family | 🟢 DTD (56) + XSD (62) + RELAX NG (56) + Schematron (40) |
| XSLT 1.0 engine | 🟢 103 tests; full instruction surface; end-to-end transforms |
| C headers | 🟢 45+19 headers, gcc & clang, zero warnings |
| CLI parity | 🟢 `xsltproc` (Phase 9), `xmllint` + `xmlcatalog` (Phase 10) complete with upstream exit codes and differential oracle parity |
| EXSLT | 🟢 All seven modules registered (34 tests); `exsl:node-set` on RTFs, math/set/str/dyn/date/func verified end-to-end |
| Historical atlas | 🟢 Semantic epochs (E-001…E-008) + release manifests + API/ABI snapshots; `atlas/SEMANTIC_EPOCHS.md` correlates every behavior change with its upstream commit |
| Oracle infrastructure | 🟢 Docker oracle built and verified; 12 historical libxml2 + 5 libxslt oracles built from archaeology git with era-tolerant build tooling (`oracle/historical/`) |
| Court coverage | 🟢 ABI courts passing; differential suites staged |
| Downstream testing | 🔴 Not started (Phase 12) |

See [`atlas/PARITY_MATRIX.md`](atlas/PARITY_MATRIX.md) for the detailed,
evidence-bounded parity matrix and [`atlas/RESIDUAL_LEDGER.md`](atlas/RESIDUAL_LEDGER.md)
for the residual ledger.